mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-04 12:47:14 +02:00
fix(v2): navbar dropdown opened with tab, not closing on click outside (#3240)
* package use-onclickoutside added * fix:dropdown toggle updated and tab behavior fixed * fix:variable name and type checker on drop down updated * fix: optional chaining added to dropdown toggle function * fix: package.json problem fixed and type of element updated * fix:type problem fixed
This commit is contained in:
parent
ee4a3ece57
commit
ee6dee72b7
3 changed files with 56 additions and 11 deletions
|
@ -27,6 +27,7 @@
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
"react-toggle": "^4.1.1",
|
"react-toggle": "^4.1.1",
|
||||||
|
"use-onclickoutside": "^0.3.1",
|
||||||
"@docusaurus/utils-validation": "^2.0.0-alpha.61"
|
"@docusaurus/utils-validation": "^2.0.0-alpha.61"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {ComponentProps, ComponentType} from 'react';
|
import React, {ComponentProps, ComponentType, useState} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||||
|
import useOnClickOutside from 'use-onclickoutside';
|
||||||
|
|
||||||
function NavLink({
|
function NavLink({
|
||||||
activeBasePath,
|
activeBasePath,
|
||||||
|
@ -33,6 +34,7 @@ function NavLink({
|
||||||
const toUrl = useBaseUrl(to);
|
const toUrl = useBaseUrl(to);
|
||||||
const activeBaseUrl = useBaseUrl(activeBasePath);
|
const activeBaseUrl = useBaseUrl(activeBasePath);
|
||||||
const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true});
|
const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
{...(href
|
{...(href
|
||||||
|
@ -61,6 +63,21 @@ function NavLink({
|
||||||
}
|
}
|
||||||
|
|
||||||
function NavItemDesktop({items, position, className, ...props}) {
|
function NavItemDesktop({items, position, className, ...props}) {
|
||||||
|
const dropDownRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
const dropDownMenuRef = React.useRef<HTMLUListElement>(null);
|
||||||
|
const [showDropDown, setShowDropDown] = useState(false);
|
||||||
|
useOnClickOutside(dropDownRef, () => toggle(false));
|
||||||
|
function toggle(state: boolean) {
|
||||||
|
if (state) {
|
||||||
|
const firstNavLinkOfULElement =
|
||||||
|
dropDownMenuRef?.current?.firstChild?.firstChild;
|
||||||
|
|
||||||
|
if (firstNavLinkOfULElement) {
|
||||||
|
(firstNavLinkOfULElement as HTMLElement).focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setShowDropDown(state);
|
||||||
|
}
|
||||||
const navLinkClassNames = (extraClassName, isDropdownItem = false) =>
|
const navLinkClassNames = (extraClassName, isDropdownItem = false) =>
|
||||||
clsx(
|
clsx(
|
||||||
{
|
{
|
||||||
|
@ -76,32 +93,34 @@ function NavItemDesktop({items, position, className, ...props}) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={dropDownRef}
|
||||||
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
|
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
|
||||||
'dropdown--left': position === 'left',
|
'dropdown--left': position === 'left',
|
||||||
'dropdown--right': position === 'right',
|
'dropdown--right': position === 'right',
|
||||||
|
'dropdown--show': showDropDown,
|
||||||
})}>
|
})}>
|
||||||
<NavLink
|
<NavLink
|
||||||
className={navLinkClassNames(className)}
|
className={navLinkClassNames(className)}
|
||||||
{...props}
|
{...props}
|
||||||
onClick={props.to ? undefined : (e) => e.preventDefault()}
|
onClick={props.to ? undefined : (e) => e.preventDefault()}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
function toggle() {
|
if ((e.key === 'Enter' && !props.to) || e.key === 'Tab') {
|
||||||
((e.target as HTMLElement)
|
e.preventDefault();
|
||||||
.parentNode as HTMLElement).classList.toggle('dropdown--show');
|
toggle(true);
|
||||||
}
|
|
||||||
if (e.key === 'Enter' && !props.to) {
|
|
||||||
toggle();
|
|
||||||
}
|
|
||||||
if (e.key === 'Tab') {
|
|
||||||
toggle();
|
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{props.label}
|
{props.label}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<ul className="dropdown__menu">
|
<ul ref={dropDownMenuRef} className="dropdown__menu">
|
||||||
{items.map(({className: childItemClassName, ...childItemProps}, i) => (
|
{items.map(({className: childItemClassName, ...childItemProps}, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<NavLink
|
<NavLink
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (i === items.length - 1 && e.key === 'Tab') {
|
||||||
|
e.preventDefault();
|
||||||
|
toggle(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
activeClassName="dropdown__link--active"
|
activeClassName="dropdown__link--active"
|
||||||
className={navLinkClassNames(childItemClassName, true)}
|
className={navLinkClassNames(childItemClassName, true)}
|
||||||
{...childItemProps}
|
{...childItemProps}
|
||||||
|
|
25
yarn.lock
25
yarn.lock
|
@ -4947,6 +4947,11 @@ archiver@^4.0.0:
|
||||||
tar-stream "^2.1.2"
|
tar-stream "^2.1.2"
|
||||||
zip-stream "^3.0.1"
|
zip-stream "^3.0.1"
|
||||||
|
|
||||||
|
are-passive-events-supported@^1.1.0:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/are-passive-events-supported/-/are-passive-events-supported-1.1.1.tgz#3db180a1753a2186a2de50a32cded3ac0979f5dc"
|
||||||
|
integrity sha512-5wnvlvB/dTbfrCvJ027Y4L4gW/6Mwoy1uFSavney0YO++GU+0e/flnjiBBwH+1kh7xNCgCOGvmJC3s32joYbww==
|
||||||
|
|
||||||
are-we-there-yet@~1.1.2:
|
are-we-there-yet@~1.1.2:
|
||||||
version "1.1.5"
|
version "1.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
|
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
|
||||||
|
@ -21105,6 +21110,26 @@ url@^0.11.0:
|
||||||
punycode "1.3.2"
|
punycode "1.3.2"
|
||||||
querystring "0.2.0"
|
querystring "0.2.0"
|
||||||
|
|
||||||
|
use-isomorphic-layout-effect@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.0.0.tgz#f56b4ed633e1c21cd9fc76fe249002a1c28989fb"
|
||||||
|
integrity sha512-JMwJ7Vd86NwAt1jH7q+OIozZSIxA4ND0fx6AsOe2q1H8ooBUp5aN6DvVCqZiIaYU6JaMRJGyR0FO7EBCIsb/Rg==
|
||||||
|
|
||||||
|
use-latest@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.1.0.tgz#7bf9684555869c3f5f37e10d0884c8accf4d3aa6"
|
||||||
|
integrity sha512-gF04d0ZMV3AMB8Q7HtfkAWe+oq1tFXP6dZKwBHQF5nVXtGsh2oAYeeqma5ZzxtlpOcW8Ro/tLcfmEodjDeqtuw==
|
||||||
|
dependencies:
|
||||||
|
use-isomorphic-layout-effect "^1.0.0"
|
||||||
|
|
||||||
|
use-onclickoutside@^0.3.1:
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-onclickoutside/-/use-onclickoutside-0.3.1.tgz#fdd723a6a499046b6bc761e4a03af432eee5917b"
|
||||||
|
integrity sha512-aahvbW5+G0XJfzj31FJeLsvc6qdKbzeTsQ8EtkHHq5qTg6bm/qkJeKLcgrpnYeHDDbd7uyhImLGdkbM9BRzOHQ==
|
||||||
|
dependencies:
|
||||||
|
are-passive-events-supported "^1.1.0"
|
||||||
|
use-latest "^1.0.0"
|
||||||
|
|
||||||
use@^3.1.0:
|
use@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
|
|
Loading…
Add table
Reference in a new issue