Skip to content

Commit

Permalink
chore: add shadowed nav item, only make item clickable if no children
Browse files Browse the repository at this point in the history
  • Loading branch information
roadlittledawn committed Oct 25, 2021
1 parent 47b5860 commit 16e539f
Showing 1 changed file with 181 additions and 0 deletions.
181 changes: 181 additions & 0 deletions src/@newrelic/gatsby-theme-newrelic/components/NavItem.js
@@ -0,0 +1,181 @@
import React, { useEffect, useLayoutEffect, useState, useMemo } from 'react';
import PropTypes from 'prop-types';
import { css } from '@emotion/react';
import { graphql, useStaticQuery } from 'gatsby';
import { useLocation } from '@reach/router';

import {
NavLink,
TextHighlight,
usePrevious,
useLocale,
useNavigation,
stripTrailingSlash,
} from '@newrelic/gatsby-theme-newrelic';

const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;

const NavItem = ({
page,
__parent: parent,
__depth: depth = 0,
__root: root,
}) => {
const locale = useLocale();
const location = useLocation();
const { searchTerm } = useNavigation();
const matchesSearch = searchTerm
? matchesSearchTerm(page, searchTerm)
: false;
const pathname = stripTrailingSlash(location.pathname).replace(
new RegExp(`\\/${locale.locale}(?=\\/)`),
''
);
const containsCurrentPage = useMemo(() => containsPage(page, pathname), [
page,
pathname,
]);
const isCurrentPage = page.url === pathname;
const shouldExpand = isCurrentPage || containsCurrentPage;
const hasChangedPage = pathname !== usePrevious(pathname);
const [isExpanded, setIsExpanded] = useState(shouldExpand);
const toggle = (expanded) => !expanded;

const {
site: {
layout: { mobileBreakpoint },
},
} = useStaticQuery(graphql`
query {
site {
layout {
mobileBreakpoint
}
}
}
`);

useEffect(() => {
if (hasChangedPage) {
setIsExpanded(shouldExpand);
}
}, [hasChangedPage, shouldExpand]);

useEffect(() => {
if (matchesSearch && !shouldExpand) {
setIsExpanded(true);
}
}, [matchesSearch, shouldExpand, searchTerm]);

useIsomorphicLayoutEffect(() => {
if (!searchTerm) {
setIsExpanded(shouldExpand);
}
}, [searchTerm, shouldExpand]);

return (
<div
css={css`
--icon-size: 1.5rem;
--icon-spacing: 0.5rem;
--nav-link-padding: 1rem;
display: ${matchesSearch || !searchTerm ? 'block' : 'none'};
padding-left: ${parent == null ? '0' : 'var(--nav-link-padding)'};
${mobileBreakpoint &&
css`
@media screen and (max-width: ${mobileBreakpoint}) {
padding-left: 0;
}
`}
`}
>
<NavLink
active={isCurrentPage}
to={page.pages?.length > 0 ? null : page.url}
icon={page.icon}
isExpanded={isExpanded}
expandable={page.pages?.length > 0}
onClick={() => {
setIsExpanded(() => !isExpanded);
}}
onToggle={() => setIsExpanded(toggle)}
mobileBreakpoint={mobileBreakpoint}
css={css`
padding-left: ${root?.icon
? 'calc(var(--icon-size) + var(--icon-spacing))'
: 'var(--nav-link-padding)'};
${mobileBreakpoint &&
css`
@media screen and (max-width: ${mobileBreakpoint}) {
--border-width: 4px;
padding-left: ${getMobilePadding({ parent, depth })};
}
`}
`}
>
{searchTerm ? (
<TextHighlight text={page.title} match={searchTerm} />
) : (
page.title
)}
</NavLink>

{isExpanded &&
page.pages?.map((child) => (
<NavItem
key={child.url || child.title}
page={child}
__parent={page}
__depth={depth + 1}
__root={depth === 0 ? page : root}
/>
))}
</div>
);
};

const page = PropTypes.shape({
title: PropTypes.string.isRequired,
icon: PropTypes.string,
url: PropTypes.string,
pages: PropTypes.arrayOf(PropTypes.object),
});

NavItem.propTypes = {
__parent: page,
__depth: PropTypes.number,
page: page.isRequired,
__root: page,
};

const getMobilePadding = ({ parent, depth }) => {
if (parent == null) {
return 'calc(var(--nav-link-padding) - var(--border-width))';
}

return parent?.icon
? `calc(${depth} * var(--nav-link-padding) + var(--icon-size) + var(--icon-spacing) - var(--border-width))`
: `calc(${depth + 1} * var(--nav-link-padding) - var(--border-width))`;
};

const containsPage = (page, url) => {
if (page.url === url) {
return true;
}

if (page.pages == null || page.pages.length === 0) {
return false;
}

return page.pages.some((child) => containsPage(child, url));
};

const matchesSearchTerm = (page, searchTerm) =>
new RegExp(searchTerm, 'i').test(page.title) ||
(page.pages || []).some((child) => matchesSearchTerm(child, searchTerm));

export default NavItem;

0 comments on commit 16e539f

Please sign in to comment.