@primer/doctocat-nextjs 0.5.3 → 0.5.4-rc.3b1107b

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @primer/doctocat-nextjs
2
2
 
3
+ ## 0.5.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#48](https://github.com/primer/doctocat-nextjs/pull/48) [`ce73c24`](https://github.com/primer/doctocat-nextjs/commit/ce73c24b2e4e924667bf7446a504bd88d8f2ccf0) Thanks [@rezrah](https://github.com/rezrah)! - - Fix inline code font-size in markdown headings. Now inherits size used in the heading.
8
+
9
+ - Increased spacing below React code blocks, which was previously too small.
10
+
11
+ - [#50](https://github.com/primer/doctocat-nextjs/pull/50) [`5d67989`](https://github.com/primer/doctocat-nextjs/commit/5d679895408c1a58342419692db4234dfddefd80) Thanks [@rezrah](https://github.com/rezrah)! - Add `menu-position` frontmatter support for custom sidebar navigation ordering
12
+
3
13
  ## 0.5.3
4
14
 
5
15
  ### Patch Changes
@@ -1,4 +1,5 @@
1
1
  .CodeBlock {
2
+ --spacing: var(--base-size-40);
2
3
  margin-block: var(--base-size-24);
3
4
  background-color: var(--brand-color-canvas-default);
4
5
  border: var(--brand-borderWidth-thin) solid var(--brand-color-border-default);
@@ -1,5 +1,5 @@
1
1
  'use client'
2
- import React, {PropsWithChildren, useCallback, useState} from 'react'
2
+ import React, {type PropsWithChildren, useCallback, useState, useRef, useEffect} from 'react'
3
3
  import clsx from 'clsx'
4
4
  import {LiveProvider, LiveEditor, LiveError, LivePreview} from 'react-live'
5
5
  import {useColorMode} from '../../context/color-modes/useColorMode'
@@ -18,12 +18,23 @@ type ReactCodeBlockProps = {
18
18
  jsxScope: Record<string, unknown>
19
19
  } & PropsWithChildren<HTMLElement>
20
20
 
21
+ const getFocusableElements = () => {
22
+ const focusableElementsQuery = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
23
+
24
+ return Array.from(document.querySelectorAll<HTMLElement>(focusableElementsQuery)).filter(el => {
25
+ const style = window.getComputedStyle(el)
26
+ return style.display !== 'none' && style.visibility !== 'hidden' && !el.hasAttribute('disabled')
27
+ })
28
+ }
29
+
21
30
  export function ReactCodeBlock(props: ReactCodeBlockProps) {
22
31
  const {colorMode, setColorMode} = useColorMode()
23
32
  const {basePath} = useConfig()
24
33
  const initialCode = getCodeFromChildren(props.children)
25
34
  const [code, setCode] = useState(initialCode)
26
35
  const shouldShowPreview = ['tsx', 'jsx'].includes(props['data-language'])
36
+ const editorRef = useRef<HTMLDivElement>(null)
37
+ const resetButtonRef = useRef<HTMLButtonElement>(null)
27
38
 
28
39
  /**
29
40
  * Transforms code to prepend basePath to img src attributes
@@ -43,6 +54,41 @@ export function ReactCodeBlock(props: ReactCodeBlockProps) {
43
54
 
44
55
  const noInline = props['data-filename'] === 'noinline' || false
45
56
 
57
+ useEffect(() => {
58
+ const editor = editorRef.current
59
+
60
+ if (!editor) return
61
+
62
+ const onKeyDown = (e: KeyboardEvent) => {
63
+ if (e.key !== 'Tab') {
64
+ return
65
+ }
66
+
67
+ if (e.shiftKey) {
68
+ e.preventDefault()
69
+ // We know that the previous focusable element is always the reset button
70
+ resetButtonRef.current?.focus()
71
+ return
72
+ }
73
+
74
+ const focusableElements = getFocusableElements()
75
+
76
+ const currentIndex = focusableElements.findIndex(el => el === resetButtonRef.current)
77
+
78
+ if (currentIndex !== -1) {
79
+ e.preventDefault()
80
+ const nextIndex = currentIndex + 1
81
+ focusableElements[nextIndex]?.focus()
82
+ }
83
+ }
84
+
85
+ editor.addEventListener('keydown', onKeyDown)
86
+
87
+ return () => {
88
+ editor.removeEventListener('keydown', onKeyDown)
89
+ }
90
+ }, [])
91
+
46
92
  return (
47
93
  <>
48
94
  <LiveProvider transformCode={transformCodeWithBasePath} code={code} scope={props.jsxScope} noInline={noInline}>
@@ -76,11 +122,11 @@ export function ReactCodeBlock(props: ReactCodeBlockProps) {
76
122
  <Button size="small" leadingVisual={CopyIcon} onClick={handleCopy}>
77
123
  Copy
78
124
  </Button>
79
- <Button size="small" leadingVisual={UndoIcon} onClick={handleReset}>
125
+ <Button size="small" leadingVisual={UndoIcon} onClick={handleReset} ref={resetButtonRef}>
80
126
  Reset
81
127
  </Button>
82
128
  </div>
83
- <div className={styles.Editor}>
129
+ <div className={styles.Editor} ref={editorRef}>
84
130
  <LiveEditor theme={colorMode === 'light' ? lightTheme : darkTheme} onChange={setCode} />
85
131
  </div>
86
132
  {shouldShowPreview && (
@@ -82,7 +82,29 @@ export function Sidebar({pageMap}: SidebarProps) {
82
82
  <NextLink href={item.route}>{subNavName}</NextLink>
83
83
  </NavList.GroupHeading>
84
84
  {item.children
85
- .sort((a, b) => ((a as MdxFile).name === 'index' ? -1 : (b as MdxFile).name === 'index' ? 1 : 0)) // puts index page first
85
+ .sort((a, b) => {
86
+ // make sure index page is first
87
+ if ((a as MdxFile).name === 'index') return -1
88
+ if ((b as MdxFile).name === 'index') return 1
89
+
90
+ // Check for menu-position property in frontmatter
91
+ const aPos = (a as MdxFile).frontMatter?.['menu-position']
92
+ const bPos = (b as MdxFile).frontMatter?.['menu-position']
93
+
94
+ // If both have menu-position, sort by menu-position
95
+ if (typeof aPos === 'number' && typeof bPos === 'number') {
96
+ return aPos - bPos
97
+ }
98
+
99
+ // If only one has menu-position, it comes first
100
+ if (typeof aPos === 'number') return -1
101
+ if (typeof bPos === 'number') return 1
102
+
103
+ // Neither has menu-position, sort alphabetically by title or name
104
+ const aTitle = (a as MdxFile).frontMatter?.title || (a as MdxFile).name
105
+ const bTitle = (b as MdxFile).frontMatter?.title || (b as MdxFile).name
106
+ return aTitle.localeCompare(bTitle)
107
+ })
86
108
  // only show index page if it has show-tabs
87
109
  .filter(child => (child as MdxFile).name !== 'index' || hasShowTabs(child as ExtendedPageItem))
88
110
  .map(child => {
@@ -211,6 +211,11 @@
211
211
  background-color: var(--base-color-scale-indigo-0);
212
212
  }
213
213
 
214
+ /* Inline code inside headings should inherit font-size */
215
+ .Prose :is(h1, h2, h3, h4, h5, h6):has(code) code:not(:global(.custom-component) code) {
216
+ font-size: inherit;
217
+ }
218
+
214
219
  [data-color-mode='dark'] .Prose code:not(:global(.custom-component) code) {
215
220
  background-color: var(--base-color-scale-indigo-9);
216
221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/doctocat-nextjs",
3
- "version": "0.5.3",
3
+ "version": "0.5.4-rc.3b1107b",
4
4
  "description": "A Next.js theme for building Primer documentation sites",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/types.ts CHANGED
@@ -31,6 +31,7 @@ export type FrontMatter = {
31
31
  description?: string
32
32
  filePath?: string
33
33
  keywords?: string[]
34
+ menu_position?: number
34
35
  related?: {
35
36
  title: string
36
37
  href: string