Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🧭 Move collapsed top-bar to primary sidebar on small screens #444

Merged
merged 17 commits into from
Aug 23, 2024
Merged
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
7 changes: 7 additions & 0 deletions .changeset/forty-carrots-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@myst-theme/frontmatter': patch
'@myst-theme/site': patch
'@myst-theme/book': patch
---

Some alignment fixes, leaving more control over content top alignment to the theme
6 changes: 6 additions & 0 deletions .changeset/mighty-ears-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@myst-theme/site': patch
'@myst-theme/book': patch
---

Renamed `Navigation` component and split for re-use in different (composed/multi-site) themes
6 changes: 6 additions & 0 deletions .changeset/olive-baboons-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@myst-theme/site': minor
'@myst-theme/book': patch
---

Rework TOC to PrimarySidebar
5 changes: 5 additions & 0 deletions .changeset/strong-cars-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@myst-theme/common': patch
---

Modified `getProjectHeadings` to work with plain `projectSlugs` to support custom theme routes that use `baseurl` but have no separate project.
5 changes: 4 additions & 1 deletion packages/common/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ export function getProjectHeadings(
},
...project.pages.map((p) => {
if (!('slug' in p)) return p;
return { ...p, path: projectSlug ? `/${project.slug}/${p.slug}` : `/${p.slug}` };
return {
...p,
path: projectSlug && project.slug ? `/${project.slug}/${p.slug}` : `/${p.slug}`,
};
}),
];
if (opts.addGroups) {
Expand Down
2 changes: 1 addition & 1 deletion packages/frontmatter/src/FrontmatterBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ export function FrontmatterBlock({
className={classNames(className)}
>
{showHeaderBlock && (
<div className="flex items-center h-6 mt-3 mb-5 text-sm font-light">
<div className="flex items-center h-6 mb-5 text-sm font-light">
{subject && (
<div
className={classNames('flex-none pr-2 smallcaps', {
Expand Down
4 changes: 2 additions & 2 deletions packages/site/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1077,10 +1077,10 @@
- 8af808bf: Remove css updates for document nav and outline from React state

```tsx
const { container, toc } = useTocHeight(top);
const { container, toc } = useSidebarHeight(top);

// Update the nav and article, removing the height
<Navigation tocRef={toc} ... />
<Navigation sidebarRef={toc} ... />
<article ref={container} ... />
```

Expand Down
6 changes: 5 additions & 1 deletion packages/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
"license": "MIT",
"sideEffects": false,
"scripts": {
"clean": "rimraf dist",
"compile": "tsc --noEmit",
"lint": "eslint src/**/*.ts*",
"lint:format": "prettier --check \"src/**/*.{ts,tsx,md}\""
"lint:format": "prettier --check \"src/**/*.{ts,tsx,md}\"",
"dev": "npm-run-all --parallel \"build:* -- --watch\"",
"build:esm": "tsc",
"build": "npm-run-all -l clean -p build:esm"
},
"dependencies": {
"@headlessui/react": "^1.7.15",
Expand Down
2 changes: 1 addition & 1 deletion packages/site/src/components/Headers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function ArticleHeader({
frontmatter={rest}
authorStyle="list"
className={classNames('flex-grow', {
'pt-4 px-6': frontmatter?.banner,
'pt-6 px-6': frontmatter?.banner,
...positionFrontmatter,
})}
hideBadges
Expand Down
25 changes: 25 additions & 0 deletions packages/site/src/components/Navigation/InlineTableOfContents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useSiteManifest } from '@myst-theme/providers';
import { getProjectHeadings } from '@myst-theme/common';
import { Toc } from './TableOfContentsItems.js';

export const InlineTableOfContents = ({
projectSlug,
sidebarRef,
className = 'flex-grow overflow-y-auto max-w-[350px]',
}: {
projectSlug?: string;
className?: string;
sidebarRef?: React.RefObject<HTMLElement>;
}) => {
const config = useSiteManifest();
if (!config) return null;
const headings = getProjectHeadings(config, projectSlug, {
addGroups: false,
});
if (!headings) return null;
return (
<nav aria-label="Table of Contents" className={className} ref={sidebarRef}>
<Toc headings={headings} />
</nav>
);
};
47 changes: 47 additions & 0 deletions packages/site/src/components/Navigation/Link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { useLinkProvider, useNavLinkProvider } from '@myst-theme/providers';

export function ExternalOrInternalLink({
to,
className,
children,
nav,
onClick,
prefetch = 'intent',
}: {
to: string;
className?: string | ((props: { isActive: boolean }) => string);
children: React.ReactNode;
nav?: boolean;
onClick?: () => void;
prefetch?: 'intent' | 'render' | 'none';
}) {
const Link = useLinkProvider();
const NavLink = useNavLinkProvider();
const staticClass = typeof className === 'function' ? className({ isActive: false }) : className;
if (to.startsWith('http') || to.startsWith('mailto:')) {
return (
<a
href={to}
target="_blank"
rel="noopener noreferrer"
className={staticClass}
onClick={onClick}
>
{children}
</a>
);
}
if (nav) {
return (
<NavLink prefetch={prefetch} to={to} className={className} onClick={onClick}>
{children}
</NavLink>
);
}
return (
<Link prefetch={prefetch} to={to} className={staticClass} onClick={onClick}>
{children}
</Link>
);
}
87 changes: 78 additions & 9 deletions packages/site/src/components/Navigation/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,106 @@
import { useNavOpen, useThemeTop } from '@myst-theme/providers';
import { TableOfContents } from './TableOfContents.js';
import { useNavOpen, useSiteManifest, useThemeTop } from '@myst-theme/providers';
import { PrimarySidebar } from './PrimarySidebar.js';
import type { Heading } from '@myst-theme/common';
import { getProjectHeadings } from '@myst-theme/common';
import type { SiteManifest } from 'myst-config';

export function Navigation({
/**
* PrimaryNavigation will load nav links and headers from the site manifest and display
* them in a mobile-friendly format.
*/
export const PrimaryNavigation = ({
children,
projectSlug,
tocRef,
sidebarRef,
hide_toc,
mobileOnly,
footer,
}: {
children?: React.ReactNode;
projectSlug?: string;
tocRef?: React.RefObject<HTMLDivElement>;
sidebarRef?: React.RefObject<HTMLDivElement>;
hide_toc?: boolean;
mobileOnly?: boolean;
footer?: React.ReactNode;
}) {
}) => {
const config = useSiteManifest();
if (!config) return null;

const headings = getProjectHeadings(config, projectSlug, {
addGroups: false,
});

const { nav } = config;

return (
<ConfigurablePrimaryNavigation
children={children}
sidebarRef={sidebarRef}
hide_toc={hide_toc}
mobileOnly={mobileOnly}
nav={nav}
headings={headings}
footer={footer}
/>
);
};

/**
@deprecated use PrimaryNavigation instead
*/
export const Navigation = PrimaryNavigation;

/**
* ConfigurablePrimaryNavigation will display a mobile-friendly navigation sidebar based on the
* nav, headings, and footer provided by the caller. Use this in situations where the PrimaryNavigation
* component may pick up the wrong SiteManifest.
*/
export const ConfigurablePrimaryNavigation = ({
children,
sidebarRef,
hide_toc,
mobileOnly,
nav,
headings,
footer,
}: {
children?: React.ReactNode;
sidebarRef?: React.RefObject<HTMLDivElement>;
hide_toc?: boolean;
mobileOnly?: boolean;
nav?: SiteManifest['nav'];
headings?: Heading[];
footer?: React.ReactNode;
}) => {
const [open, setOpen] = useNavOpen();
const top = useThemeTop();

if (children)
console.warn(
`Including children in Navigation can break keyboard accessbility and is deprecated. Please move children to the page component.`,
);

// the logic on the following line looks wrong, this will return `null` or `<></>`
// we should just return `null` if `hide_toc` is true?
if (hide_toc) return children ? null : <>{children}</>;

return (
<>
{open && (
{open && !mobileOnly && headings && (
<div
className="fixed inset-0 z-30 bg-black opacity-50"
style={{ marginTop: top }}
onClick={() => setOpen(false)}
></div>
)}
<TableOfContents tocRef={tocRef} projectSlug={projectSlug} footer={footer} />
<PrimarySidebar
sidebarRef={sidebarRef}
nav={nav}
headings={headings}
footer={footer}
mobileOnly={mobileOnly}
/>
{children}
</>
);
}
};
Loading
Loading