@myst-theme/site 0.1.35 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myst-theme/site",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "main": "./src/index.ts",
5
5
  "types": "./src/index.ts",
6
6
  "files": [
@@ -16,16 +16,16 @@
16
16
  "dependencies": {
17
17
  "@headlessui/react": "^1.7.13",
18
18
  "@heroicons/react": "^2.0.14",
19
- "@myst-theme/diagrams": "^0.1.35",
20
- "@myst-theme/frontmatter": "^0.1.35",
21
- "@myst-theme/jupyter": "^0.1.35",
22
- "@myst-theme/providers": "^0.1.35",
19
+ "@myst-theme/diagrams": "^0.1.36",
20
+ "@myst-theme/frontmatter": "^0.1.36",
21
+ "@myst-theme/jupyter": "^0.1.36",
22
+ "@myst-theme/providers": "^0.1.36",
23
23
  "classnames": "^2.3.2",
24
24
  "lodash.throttle": "^4.1.1",
25
25
  "myst-common": "^0.0.16",
26
26
  "myst-config": "^0.0.12",
27
- "myst-demo": "^0.1.35",
28
- "myst-to-react": "^0.1.35",
27
+ "myst-demo": "^0.1.36",
28
+ "myst-to-react": "^0.1.36",
29
29
  "nbtx": "^0.2.3",
30
30
  "node-cache": "^5.1.2",
31
31
  "node-fetch": "^2.6.7",
@@ -1,3 +1,4 @@
1
+ import { useNavigation } from '@remix-run/react';
1
2
  import classNames from 'classnames';
2
3
  import throttle from 'lodash.throttle';
3
4
  import { useCallback, useEffect, useRef, useState } from 'react';
@@ -155,32 +156,51 @@ const useIntersectionObserver = (highlight: () => void, onScreen: Set<HTMLHeadin
155
156
  const DOC_OUTLINE_CLASS =
156
157
  'fixed z-10 bottom-0 right-[max(0px,calc(50%-45rem))] w-[14rem] lg:w-[18rem] py-10 px-4 lg:px-8 overflow-y-auto hidden lg:block';
157
158
 
159
+ export function useOutlineHeight<T extends HTMLElement = HTMLElement>() {
160
+ const container = useRef<T>(null);
161
+ const outline = useRef<T>(null);
162
+ const transitionState = useNavigation().state;
163
+ const setHeight = () => {
164
+ if (!container.current || !outline.current) return;
165
+ const height = container.current.offsetHeight - window.scrollY;
166
+ outline.current.style.display = height < 50 ? 'none' : '';
167
+ outline.current.style.height = height > window.innerHeight ? '' : `${height}px`;
168
+ outline.current.style.opacity = height && height > 300 ? '' : '0';
169
+ outline.current.style.pointerEvents = height && height > 300 ? '' : 'none';
170
+ };
171
+ useEffect(() => {
172
+ setHeight();
173
+ setTimeout(setHeight, 100); // Some lag sometimes
174
+ const handleScroll = () => setHeight();
175
+ window.addEventListener('scroll', handleScroll);
176
+ return () => {
177
+ window.removeEventListener('scroll', handleScroll);
178
+ };
179
+ }, [container, outline, transitionState]);
180
+ return { container, outline };
181
+ }
182
+
158
183
  export const DocumentOutline = ({
184
+ outlineRef,
159
185
  top,
160
- height,
161
186
  className = DOC_OUTLINE_CLASS,
162
187
  }: {
188
+ outlineRef?: React.RefObject<HTMLElement>;
163
189
  top?: number;
164
190
  height?: number;
165
191
  className?: string;
166
192
  }) => {
167
193
  const { activeId, headings, highlight } = useHeaders();
168
- if (height && height < 50) return null;
169
194
  if (headings.length <= 1) return <nav suppressHydrationWarning />;
170
195
  return (
171
196
  <nav
197
+ ref={outlineRef}
172
198
  aria-label="Document Outline"
173
199
  suppressHydrationWarning
174
200
  className={classNames('not-prose transition-opacity overflow-y-auto', className)}
175
201
  style={{
176
202
  top: top ?? 0,
177
- height:
178
- typeof document === 'undefined' || (height && height > window.innerHeight)
179
- ? undefined
180
- : height,
181
203
  maxHeight: `calc(100vh - ${(top ?? 0) + 20}px)`,
182
- opacity: height && height > 300 ? undefined : 0,
183
- pointerEvents: height && height > 300 ? undefined : 'none',
184
204
  }}
185
205
  >
186
206
  <div className="text-slate-900 mb-4 text-sm leading-6 dark:text-slate-100 uppercase">
@@ -1,6 +1,7 @@
1
1
  import classNames from 'classnames';
2
2
  import { Link } from '@remix-run/react';
3
- import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
3
+ import ArrowLeftIcon from '@heroicons/react/24/outline/ArrowLeftIcon';
4
+ import ArrowRightIcon from '@heroicons/react/24/outline/ArrowRightIcon';
4
5
  import type { FooterLinks, NavigationLink } from '../types';
5
6
  import { useUrlbase, withUrlbase } from '@myst-theme/providers';
6
7
 
@@ -5,14 +5,14 @@ export function Navigation({
5
5
  children,
6
6
  projectSlug,
7
7
  top,
8
- height,
8
+ tocRef,
9
9
  hide_toc,
10
10
  footer,
11
11
  }: {
12
12
  children?: React.ReactNode;
13
13
  projectSlug?: string;
14
14
  top?: number;
15
- height?: number;
15
+ tocRef?: React.RefObject<HTMLDivElement>;
16
16
  hide_toc?: boolean;
17
17
  footer?: React.ReactNode;
18
18
  }) {
@@ -23,11 +23,12 @@ export function Navigation({
23
23
  {open && (
24
24
  <div
25
25
  className="fixed inset-0 bg-black opacity-50 z-30"
26
+ style={{ marginTop: top }}
26
27
  onClick={() => setOpen(false)}
27
28
  ></div>
28
29
  )}
29
30
  {children}
30
- <TableOfContents projectSlug={projectSlug} top={top} height={height} footer={footer} />
31
+ <TableOfContents tocRef={tocRef} projectSlug={projectSlug} top={top} footer={footer} />
31
32
  </>
32
33
  );
33
34
  }
@@ -1,6 +1,6 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import classNames from 'classnames';
3
- import { NavLink, useParams, useLocation } from '@remix-run/react';
3
+ import { NavLink, useParams, useLocation, useNavigation } from '@remix-run/react';
4
4
  import type { SiteManifest } from 'myst-config';
5
5
  import { useNavOpen, useSiteManifest, useUrlbase, withUrlbase } from '@myst-theme/providers';
6
6
  import { getProjectHeadings } from '../../loaders';
@@ -102,14 +102,39 @@ const Headings = ({ folder, headings, sections }: Props) => {
102
102
  );
103
103
  };
104
104
 
105
+ export function useTocHeight<T extends HTMLElement = HTMLElement>(top?: number) {
106
+ const container = useRef<T>(null);
107
+ const toc = useRef<HTMLDivElement>(null);
108
+ const transitionState = useNavigation().state;
109
+ const setHeight = () => {
110
+ if (!container.current || !toc.current) return;
111
+ const height = container.current.offsetHeight - window.scrollY;
112
+ const div = toc.current.firstChild as HTMLDivElement;
113
+ const MAGIC_PADDING = 16; // I dunno, just go with it ...
114
+ if (div) div.style.height = `min(calc(100vh - ${top ?? 0}px), ${height + MAGIC_PADDING}px)`;
115
+ const nav = toc.current.querySelector('nav');
116
+ if (nav) nav.style.opacity = height > 150 ? '1' : '0';
117
+ };
118
+ useEffect(() => {
119
+ setHeight();
120
+ setTimeout(setHeight, 100); // Some lag sometimes
121
+ const handleScroll = () => setHeight();
122
+ window.addEventListener('scroll', handleScroll);
123
+ return () => {
124
+ window.removeEventListener('scroll', handleScroll);
125
+ };
126
+ }, [container, toc, transitionState]);
127
+ return { container, toc };
128
+ }
129
+
105
130
  export const TableOfContents = ({
106
131
  projectSlug,
107
132
  top,
108
- height,
133
+ tocRef,
109
134
  footer,
110
135
  }: {
111
136
  top?: number;
112
- height?: number;
137
+ tocRef?: React.RefObject<HTMLDivElement>;
113
138
  projectSlug?: string;
114
139
  footer?: React.ReactNode;
115
140
  }) => {
@@ -124,13 +149,13 @@ export const TableOfContents = ({
124
149
  if (!headings) return null;
125
150
  return (
126
151
  <div
152
+ ref={tocRef}
127
153
  className={classNames(
128
154
  'fixed xl:article-grid article-grid-gap xl:w-screen z-30 xl:pointer-events-none overflow-auto max-xl:min-w-[300px]',
129
155
  { hidden: !open },
130
156
  )}
131
157
  style={{
132
158
  top: top ?? 0,
133
- height: `min(calc(100vh - ${top ?? 0}px), ${height}px)`,
134
159
  }}
135
160
  >
136
161
  <div
@@ -149,7 +174,6 @@ export const TableOfContents = ({
149
174
  <nav
150
175
  aria-label="Table of Contents"
151
176
  className="flex-grow overflow-y-auto transition-opacity mt-6 pb-3 ml-3 xl:ml-0 mr-3"
152
- style={{ opacity: height && height > 150 ? undefined : 0 }}
153
177
  >
154
178
  <Headings folder={resolvedProjectSlug} headings={headings} sections={config?.projects} />
155
179
  </nav>
@@ -1,6 +1,6 @@
1
1
  import { useTheme } from '@myst-theme/providers';
2
- import { MoonIcon } from '@heroicons/react/24/solid';
3
- import { SunIcon } from '@heroicons/react/24/outline';
2
+ import MoonIcon from '@heroicons/react/24/solid/MoonIcon';
3
+ import SunIcon from '@heroicons/react/24/outline/SunIcon';
4
4
  import classNames from 'classnames';
5
5
 
6
6
  export function ThemeButton({ className = 'mx-3 h-8 w-8' }: { className?: string }) {
@@ -8,7 +8,7 @@ export function ThemeButton({ className = 'mx-3 h-8 w-8' }: { className?: string
8
8
  return (
9
9
  <button
10
10
  className={classNames(
11
- 'theme rounded-full border border-white border-solid overflow-hidden text-white hover:text-stone-500 hover:bg-white',
11
+ 'theme rounded-full border border-stone-700 dark:border-white hover:bg-neutral-100 border-solid overflow-hidden text-stone-700 dark:text-white hover:text-stone-500 dark:hover:text-neutral-800',
12
12
  className,
13
13
  )}
14
14
  title={`Change theme to ${isDark ? 'light' : 'dark'} mode.`}
@@ -2,11 +2,9 @@ import { Link, NavLink } from '@remix-run/react';
2
2
  import { Fragment } from 'react';
3
3
  import classNames from 'classnames';
4
4
  import { Menu, Transition } from '@headlessui/react';
5
- import {
6
- EllipsisVerticalIcon,
7
- Bars3Icon as MenuIcon,
8
- ChevronDownIcon,
9
- } from '@heroicons/react/24/solid';
5
+ import EllipsisVerticalIcon from '@heroicons/react/24/solid/EllipsisVerticalIcon';
6
+ import MenuIcon from '@heroicons/react/24/solid/Bars3Icon';
7
+ import ChevronDownIcon from '@heroicons/react/24/solid/ChevronDownIcon';
10
8
  import type { SiteManifest, SiteNavItem } from 'myst-config';
11
9
  import { ThemeButton } from './ThemeButton';
12
10
  import { useNavOpen, useSiteManifest } from '@myst-theme/providers';
@@ -58,7 +56,7 @@ function NavItem({ item }: { item: SiteNavItem }) {
58
56
  to={item.url ?? ''}
59
57
  className={({ isActive }) =>
60
58
  classNames(
61
- 'inline-flex items-center justify-center w-full mx-2 py-1 text-md font-medium text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75',
59
+ 'inline-flex items-center justify-center w-full mx-2 py-1 text-md font-medium dark:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75',
62
60
  {
63
61
  'border-b border-stone-200': isActive,
64
62
  },
@@ -73,12 +71,9 @@ function NavItem({ item }: { item: SiteNavItem }) {
73
71
  return (
74
72
  <Menu as="div" className="relative grow-0 inline-block mx-2">
75
73
  <div className="inline-block">
76
- <Menu.Button className="inline-flex items-center justify-center w-full mx-2 py-1 text-md font-medium text-white rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
74
+ <Menu.Button className="inline-flex items-center justify-center w-full mx-2 py-1 text-md font-medium text-stone-900 dark:text-white rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
77
75
  <span>{item.title}</span>
78
- <ChevronDownIcon
79
- className="w-5 h-5 ml-2 -mr-1 text-violet-200 hover:text-violet-100"
80
- aria-hidden="true"
81
- />
76
+ <ChevronDownIcon className="w-5 h-5 ml-2 -mr-1 text-violet-200 hover:text-violet-100" />
82
77
  </Menu.Button>
83
78
  </div>
84
79
  <Transition
@@ -184,11 +179,15 @@ function HomeLink({ logo, logoText, name }: { logo?: string; logoText?: string;
184
179
  const nothingSet = !logo && !logoText;
185
180
  return (
186
181
  <Link
187
- className="flex items-center text-white w-fit ml-3 md:ml-5 xl:ml-7"
182
+ className="flex items-center dark:text-white w-fit ml-3 md:ml-5 xl:ml-7"
188
183
  to="/"
189
184
  prefetch="intent"
190
185
  >
191
- {logo && <img src={logo} className="h-9 mr-3" alt={logoText || name} height="2.25rem"></img>}
186
+ {logo && (
187
+ <div className="dark:bg-white dark:rounded p-1 mr-3">
188
+ <img src={logo} className="h-9" alt={logoText || name} height="2.25rem"></img>
189
+ </div>
190
+ )}
192
191
  <span
193
192
  className={classNames('text-md sm:text-xl tracking-tight sm:mr-5', {
194
193
  'sr-only': !(logoText || nothingSet),
@@ -205,18 +204,18 @@ export function TopNav() {
205
204
  const config = useSiteManifest();
206
205
  const { logo, logo_text, logoText, actions, title, nav } = config ?? ({} as SiteManifest);
207
206
  return (
208
- <div className="bg-stone-700 p-3 md:px-8 fixed w-screen top-0 z-30 h-[60px]">
207
+ <div className="bg-white/80 backdrop-blur dark:bg-stone-900/80 shadow dark:shadow-stone-700 p-3 md:px-8 fixed w-screen top-0 z-30 h-[60px]">
209
208
  <nav className="flex items-center justify-between flex-wrap max-w-[1440px] mx-auto">
210
209
  <div className="flex flex-row xl:min-w-[19.5rem] mr-2 sm:mr-7 justify-start items-center">
211
210
  <div className="block xl:hidden">
212
211
  <button
213
- className="flex items-center text-stone-200 border-stone-400 hover:text-white"
212
+ className="flex items-center border-stone-400 text-stone-800 hover:text-stone-900 dark:text-stone-200 hover:dark:text-stone-100"
214
213
  onClick={() => {
215
214
  setOpen(!open);
216
215
  }}
217
216
  >
217
+ <MenuIcon className="h-8 w-8 p-1" />
218
218
  <span className="sr-only">Open Menu</span>
219
- <MenuIcon className="fill-current h-8 w-8 p-1" />
220
219
  </button>
221
220
  </div>
222
221
  <HomeLink name={title} logo={logo} logoText={logo_text || logoText} />
@@ -232,7 +231,7 @@ export function TopNav() {
232
231
  {actions?.map((action, index) => (
233
232
  <ExternalOrInternalLink
234
233
  key={action.url || index}
235
- className="inline-block text-md px-4 py-2 mx-1 leading-none border rounded text-white border-white hover:border-transparent hover:text-stone-500 hover:bg-white mt-0"
234
+ className="inline-block text-md px-4 py-2 mx-1 leading-none border rounded border-stone-700 dark:border-white text-stone-700 dark:text-white hover:text-stone-500 dark:hover:text-neutral-800 hover:bg-neutral-100 mt-0"
236
235
  to={action.url}
237
236
  >
238
237
  {action.title}
@@ -1,5 +1,5 @@
1
1
  export { ThemeButton } from './ThemeButton';
2
2
  export { TopNav, DEFAULT_NAV_HEIGHT } from './TopNav';
3
3
  export { Navigation } from './Navigation';
4
- export { TableOfContents } from './TableOfContents';
4
+ export { TableOfContents, useTocHeight } from './TableOfContents';
5
5
  export { LoadingBar } from './Loading';
@@ -1,5 +1,5 @@
1
1
  export { ContentBlocks } from './ContentBlocks';
2
- export { DocumentOutline } from './DocumentOutline';
2
+ export { DocumentOutline, useOutlineHeight } from './DocumentOutline';
3
3
  export { FooterLinksBlock } from './FooterLinksBlock';
4
4
  export { ContentReload } from './ContentReload';
5
5
  export { Bibliography } from './Bibliography';
package/src/index.ts CHANGED
@@ -4,4 +4,3 @@ export * from './loaders';
4
4
  export * from './components';
5
5
  export * from './pages';
6
6
  export * from './seo';
7
- export * from './hooks';
@@ -1,23 +0,0 @@
1
- import { useTransition } from '@remix-run/react';
2
- import { useEffect, useRef, useState } from 'react';
3
-
4
- export function useNavigationHeight<T extends HTMLElement = HTMLElement>() {
5
- const ref = useRef<T>(null);
6
- const [height, setHeightState] = useState(1000);
7
- const transitionState = useTransition().state;
8
- const setHeight = () => {
9
- if (ref.current) {
10
- setHeightState(ref.current.offsetHeight - window.scrollY);
11
- }
12
- };
13
- useEffect(() => {
14
- setHeight();
15
- setTimeout(setHeight, 100); // Some lag sometimes
16
- const handleScroll = () => setHeight();
17
- window.addEventListener('scroll', handleScroll);
18
- return () => {
19
- window.removeEventListener('scroll', handleScroll);
20
- };
21
- }, [ref, transitionState]);
22
- return { ref, height };
23
- }