feat(v2): allow to change location of search bar (#4199)

* feat(v2): allow to change location of search bar

* add SearchBar swizzle comment

* quickfix for NavbarItem theme config

* typing quickfix

* doc typo

Co-authored-by: slorber <lorber.sebastien@gmail.com>
This commit is contained in:
Alexey Pyltsyn 2021-02-09 18:31:52 +03:00 committed by GitHub
parent 2a12869fc0
commit b3b658f687
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 91 additions and 24 deletions

View file

@ -44,10 +44,7 @@ function Navbar(): JSX.Element {
navbar: {items, hideOnScroll, style}, navbar: {items, hideOnScroll, style},
colorMode: {disableSwitch: disableColorModeSwitch}, colorMode: {disableSwitch: disableColorModeSwitch},
} = useThemeConfig(); } = useThemeConfig();
const [sidebarShown, setSidebarShown] = useState(false); const [sidebarShown, setSidebarShown] = useState(false);
const [isSearchBarExpanded, setIsSearchBarExpanded] = useState(false);
const {isDarkTheme, setLightTheme, setDarkTheme} = useThemeContext(); const {isDarkTheme, setLightTheme, setDarkTheme} = useThemeContext();
const {navbarRef, isNavbarVisible} = useHideableNavbar(hideOnScroll); const {navbarRef, isNavbarVisible} = useHideableNavbar(hideOnScroll);
@ -73,6 +70,7 @@ function Navbar(): JSX.Element {
} }
}, [windowSize]); }, [windowSize]);
const hasSearchNavbarItem = items.some((item) => item.type === 'search');
const {leftItems, rightItems} = splitNavItemsByPosition(items); const {leftItems, rightItems} = splitNavItemsByPosition(items);
return ( return (
@ -101,9 +99,7 @@ function Navbar(): JSX.Element {
<Logo <Logo
className="navbar__brand" className="navbar__brand"
imageClassName="navbar__logo" imageClassName="navbar__logo"
titleClassName={clsx('navbar__title', { titleClassName={clsx('navbar__title')}
[styles.hideLogoText]: isSearchBarExpanded,
})}
/> />
{leftItems.map((item, i) => ( {leftItems.map((item, i) => (
<NavbarItem {...item} key={i} /> <NavbarItem {...item} key={i} />
@ -121,10 +117,7 @@ function Navbar(): JSX.Element {
onChange={onToggleChange} onChange={onToggleChange}
/> />
)} )}
<SearchBar {!hasSearchNavbarItem && <SearchBar />}
handleSearchBarToggle={setIsSearchBarExpanded}
isSearchBarExpanded={isSearchBarExpanded}
/>
</div> </div>
</div> </div>
<div <div
@ -152,7 +145,12 @@ function Navbar(): JSX.Element {
<div className="menu"> <div className="menu">
<ul className="menu__list"> <ul className="menu__list">
{items.map((item, i) => ( {items.map((item, i) => (
<NavbarItem mobile {...item} onClick={hideSidebar} key={i} /> <NavbarItem
mobile
{...(item as any)} // TODO fix typing
onClick={hideSidebar}
key={i}
/>
))} ))}
</ul> </ul>
</div> </div>

View file

@ -11,12 +11,6 @@
} }
} }
@media (max-width: 768px) {
.hideLogoText {
display: none;
}
}
.navbarHideable { .navbarHideable {
transition: transform var(--ifm-transition-fast) ease; transition: transform var(--ifm-transition-fast) ease;
} }

View file

@ -0,0 +1,23 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import type {Props} from '@theme/NavbarItem/SearchNavbarItem';
import SearchBar from '@theme/SearchBar';
import styles from './styles.module.css';
export default function SearchNavbarItem({mobile}: Props): JSX.Element | null {
if (mobile) {
return null;
}
return (
<div className={styles.searchWrapper}>
<SearchBar />
</div>
);
}

View file

@ -8,11 +8,13 @@
import React from 'react'; import React from 'react';
import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem'; import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
import LocaleDropdownNavbarItem from '@theme/NavbarItem/LocaleDropdownNavbarItem'; import LocaleDropdownNavbarItem from '@theme/NavbarItem/LocaleDropdownNavbarItem';
import SearchNavbarItem from '@theme/NavbarItem/SearchNavbarItem';
import type {Props} from '@theme/NavbarItem'; import type {Props} from '@theme/NavbarItem';
const NavbarItemComponents = { const NavbarItemComponents = {
default: () => DefaultNavbarItem, default: () => DefaultNavbarItem,
localeDropdown: () => LocaleDropdownNavbarItem, localeDropdown: () => LocaleDropdownNavbarItem,
search: () => SearchNavbarItem,
// Need to lazy load these items as we don't know for sure the docs plugin is loaded // Need to lazy load these items as we don't know for sure the docs plugin is loaded
// See https://github.com/facebook/docusaurus/issues/3360 // See https://github.com/facebook/docusaurus/issues/3360

View file

@ -0,0 +1,13 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
@media (max-width: 996px) {
.searchWrapper {
position: absolute;
right: var(--ifm-navbar-padding-horizontal);
}
}

View file

@ -5,4 +5,8 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
// By default, the classic theme does not provide any SearchBar implementation
// If you swizzled this file, it is your responsibility to provide an implementation
// Tip: swizzle the SearchBar from the Algolia theme for inspiration:
// npm run swizzle @docusaurus/theme-search-algolia SearchBar
export {default} from '@docusaurus/Noop'; export {default} from '@docusaurus/Noop';

View file

@ -312,6 +312,13 @@ declare module '@theme/NavbarItem/DefaultNavbarItem' {
export default DefaultNavbarItem; export default DefaultNavbarItem;
} }
declare module '@theme/NavbarItem/SearchNavbarItem' {
export type Props = {readonly mobile?: boolean};
const SearchNavbarItem: (props: Props) => JSX.Element;
export default SearchNavbarItem;
}
declare module '@theme/NavbarItem/LocaleDropdownNavbarItem' { declare module '@theme/NavbarItem/LocaleDropdownNavbarItem' {
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem'; import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
import type {NavLinkProps} from '@theme/NavbarItem/DefaultNavbarItem'; import type {NavLinkProps} from '@theme/NavbarItem/DefaultNavbarItem';
@ -366,13 +373,15 @@ declare module '@theme/NavbarItem' {
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem'; import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
import type {Props as DocsVersionDropdownNavbarItemProps} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; import type {Props as DocsVersionDropdownNavbarItemProps} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';
import type {Props as DocsVersionNavbarItemProps} from '@theme/NavbarItem/DocsVersionNavbarItem'; import type {Props as DocsVersionNavbarItemProps} from '@theme/NavbarItem/DocsVersionNavbarItem';
import type {Props as SearchNavbarItemProps} from '@theme/NavbarItem/SearchNavbarItem';
export type Props = export type Props =
| ({readonly type?: 'default' | undefined} & DefaultNavbarItemProps) | ({readonly type?: 'default' | undefined} & DefaultNavbarItemProps)
| ({ | ({
readonly type: 'docsVersionDropdown'; readonly type: 'docsVersionDropdown';
} & DocsVersionDropdownNavbarItemProps) } & DocsVersionDropdownNavbarItemProps)
| ({readonly type: 'docsVersion'} & DocsVersionNavbarItemProps); | ({readonly type: 'docsVersion'} & DocsVersionNavbarItemProps)
| ({readonly type: 'search'} & SearchNavbarItemProps);
const NavbarItem: (props: Props) => JSX.Element; const NavbarItem: (props: Props) => JSX.Element;
export default NavbarItem; export default NavbarItem;

View file

@ -107,6 +107,11 @@ const LocaleDropdownNavbarItemSchema = Joi.object({
className: Joi.string(), className: Joi.string(),
}); });
const SearchItemSchema = Joi.object({
type: Joi.string().equal('search').required(),
position: NavbarItemPosition,
});
// Can this be made easier? :/ // Can this be made easier? :/
const isOfType = (type) => { const isOfType = (type) => {
let typeSchema = Joi.string().required(); let typeSchema = Joi.string().required();
@ -139,6 +144,10 @@ const NavbarItemSchema = Joi.object().when({
is: isOfType('localeDropdown'), is: isOfType('localeDropdown'),
then: LocaleDropdownNavbarItemSchema, then: LocaleDropdownNavbarItemSchema,
}, },
{
is: isOfType('search'),
then: SearchItemSchema,
},
{ {
is: isOfType(undefined), is: isOfType(undefined),
then: Joi.forbidden().messages({ then: Joi.forbidden().messages({

View file

@ -8,8 +8,9 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
export type DocsVersionPersistence = 'localStorage' | 'none'; export type DocsVersionPersistence = 'localStorage' | 'none';
// TODO improve // TODO improve types, use unions
export type NavbarItem = { export type NavbarItem = {
type?: string | undefined;
items?: NavbarItem[]; items?: NavbarItem[];
label?: string; label?: string;
}; };

View file

@ -395,11 +395,25 @@ module.exports = {
}; };
``` ```
```` ### Navbar search
If you use the [search](../../search.md), the search bar will be the rightmost element in the navbar.
However, with this special navbar item type, you can change the default location.
```js {5-8} title="docusaurus.config.js"
module.exports = {
themeConfig: {
navbar: {
items: [
{ {
type: 'localeDropdown', type: 'search',
position: 'left', position: 'right',
}, },
],
},
},
};
``` ```
### Auto-hide sticky navbar ### Auto-hide sticky navbar
@ -416,7 +430,7 @@ module.exports = {
// ... // ...
}, },
}; };
```` ```
### Navbar style ### Navbar style