mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-21 13:06:58 +02:00
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:
parent
2a12869fc0
commit
b3b658f687
10 changed files with 91 additions and 24 deletions
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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';
|
||||||
|
|
11
packages/docusaurus-theme-classic/src/types.d.ts
vendored
11
packages/docusaurus-theme-classic/src/types.d.ts
vendored
|
@ -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;
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -395,12 +395,26 @@ 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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue