зеркало из https://github.com/github/docs.git
Move mini tocs rest + collapsible (#26069)
* starting minitocs change * moving rest mini tocs and adding functionality to collapse * adding some comments * update margins * minor updates: fix scroll to top button add padding to resthttpmethod verbs and background color for nested arrows in minitocs * Update components/rest/RestReferencePage.tsx Co-authored-by: Rachael Sewell <rachmari@github.com> * Update components/rest/RestReferencePage.tsx Co-authored-by: Rachael Sewell <rachmari@github.com> * fix comment * fix wording and z index * bring back articlegridlayout * updating margins for lg and xl Co-authored-by: Rachael Sewell <rachmari@github.com>
This commit is contained in:
Родитель
3b8b6e0e61
Коммит
21b59b5805
|
@ -93,7 +93,7 @@ export const DefaultLayout = (props: Props) => {
|
|||
style={{ height: '100vh' }}
|
||||
>
|
||||
<Header />
|
||||
<main id="main-content">
|
||||
<main id="main-content" style={{ scrollMarginTop: '5rem' }}>
|
||||
<DeprecationBanner />
|
||||
<RestBanner />
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ export function CodeBlock({ verb, headingLang, codeBlock, highlight }: Props) {
|
|||
>
|
||||
<code>
|
||||
{verb && (
|
||||
<span className="color-bg-accent-emphasis color-fg-on-emphasis rounded-1 text-uppercase">
|
||||
<span className="color-bg-accent-emphasis color-fg-on-emphasis rounded-1 text-uppercase p-1">
|
||||
{verb}
|
||||
</span>
|
||||
)}{' '}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
.parameterTable {
|
||||
table-layout: fixed !important;
|
||||
z-index: 0;
|
||||
|
||||
thead {
|
||||
tr {
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import cx from 'classnames'
|
||||
import { useRouter } from 'next/router'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
import { DefaultLayout } from 'components/DefaultLayout'
|
||||
import { ArticleTitle } from 'components/article/ArticleTitle'
|
||||
import { useMainContext } from 'components/context/MainContext'
|
||||
import { MarkdownContent } from 'components/ui/MarkdownContent'
|
||||
import { Lead } from 'components/ui/Lead'
|
||||
import { ArticleGridLayout } from 'components/article/ArticleGridLayout'
|
||||
import { MiniTocItem } from 'components/context/ArticleContext'
|
||||
import { RestCategoryOperationsT } from './types'
|
||||
import { RestOperation } from './RestOperation'
|
||||
import { MiniTocs } from 'components/ui/MiniTocs'
|
||||
import { ChevronDownIcon, ChevronUpIcon, SearchIcon } from '@primer/octicons-react'
|
||||
import { useTranslation } from 'components/hooks/useTranslation'
|
||||
import { ActionList } from '@primer/react'
|
||||
|
||||
const ClientSideHighlightJS = dynamic(() => import('components/article/ClientSideHighlightJS'), {
|
||||
ssr: false,
|
||||
|
@ -37,9 +38,11 @@ export const RestReferencePage = ({
|
|||
restOperations,
|
||||
miniTocItems,
|
||||
}: StructuredContentT) => {
|
||||
const { t } = useTranslation('pages')
|
||||
const { asPath } = useRouter()
|
||||
const { page } = useMainContext()
|
||||
const subcategories = Object.keys(restOperations)
|
||||
const [collapsed, setCollapsed] = useState({} as Record<number, boolean>)
|
||||
|
||||
// We have some one-off redirects for rest api docs
|
||||
// currently those are limited to the repos page, but
|
||||
|
@ -106,6 +109,43 @@ export const RestReferencePage = ({
|
|||
// consecutive one does.
|
||||
}, [asPath])
|
||||
|
||||
// Resetting the collapsed array when we move to another REST page
|
||||
useEffect(() => {
|
||||
setCollapsed({})
|
||||
}, [asPath])
|
||||
|
||||
const handleClick = (param: number) => {
|
||||
setCollapsed((prevState) => {
|
||||
return { ...prevState, [param]: !prevState[param] }
|
||||
})
|
||||
}
|
||||
|
||||
const renderTocItem = (item: MiniTocItem, index: number) => {
|
||||
return (
|
||||
<ActionList.Item
|
||||
as="li"
|
||||
key={item.contents}
|
||||
className={item.platform}
|
||||
sx={{ listStyle: 'none', padding: '2px' }}
|
||||
>
|
||||
<div className={cx('lh-condensed d-block width-full')}>
|
||||
<div className="d-inline-flex" dangerouslySetInnerHTML={{ __html: item.contents }} />
|
||||
{item.items && item.items.length > 0 && (
|
||||
<button
|
||||
className="background-transparent border-0 ml-1"
|
||||
onClick={() => handleClick(index)}
|
||||
>
|
||||
{!collapsed[index] ? <ChevronDownIcon /> : <ChevronUpIcon />}
|
||||
</button>
|
||||
)}
|
||||
{collapsed[index] && item.items && item.items.length > 0 ? (
|
||||
<ul className="ml-3">{item.items.map(renderTocItem)}</ul>
|
||||
) : null}
|
||||
</div>
|
||||
</ActionList.Item>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<DefaultLayout>
|
||||
{/* Doesn't matter *where* this is included because it will
|
||||
|
@ -113,47 +153,60 @@ export const RestReferencePage = ({
|
|||
{loadClientsideRedirectExceptions && <ClientSideRedirectExceptions />}
|
||||
{lazyLoadHighlightJS && <ClientSideHighlightJS />}
|
||||
|
||||
<div className="container-xl px-3 px-md-6 my-4">
|
||||
<ArticleGridLayout
|
||||
topper={<ArticleTitle>{page.title}</ArticleTitle>}
|
||||
intro={
|
||||
<>
|
||||
{page.introPlainText && (
|
||||
<Lead data-testid="lead" data-search="lead">
|
||||
{page.introPlainText}
|
||||
</Lead>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
toc={
|
||||
<>
|
||||
{miniTocItems && miniTocItems.length > 1 && (
|
||||
<MiniTocs pageTitle={page.title} miniTocItems={miniTocItems} />
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div key={`restCategory-introContent`}>
|
||||
<div dangerouslySetInnerHTML={{ __html: introContent }} />
|
||||
<div className="container-xl px-3 px-md-6 my-4 mx-xl-12 mx-lg-12">
|
||||
<h1>{page.title}</h1>
|
||||
{page.introPlainText && (
|
||||
<Lead data-testid="lead" data-search="lead" className="markdown-body">
|
||||
{page.introPlainText}
|
||||
</Lead>
|
||||
)}
|
||||
<div key={`restCategory-introContent`}>
|
||||
<div dangerouslySetInnerHTML={{ __html: introContent }} />
|
||||
</div>
|
||||
<div className="my-3 d-flex">
|
||||
<div className="pr-3 mt-1">
|
||||
<Circle className="color-fg-on-emphasis color-bg-emphasis">
|
||||
<SearchIcon className="" size={15} />
|
||||
</Circle>
|
||||
</div>
|
||||
<div id="article-contents">
|
||||
<MarkdownContent>
|
||||
{subcategories.map((subcategory, index) => (
|
||||
<div key={`restCategory-${index}`}>
|
||||
<div dangerouslySetInnerHTML={{ __html: descriptions[subcategory] }} />
|
||||
{restOperations[subcategory].map((operation, index) => (
|
||||
<RestOperation
|
||||
key={`restOperation-${index}`}
|
||||
operation={operation}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</MarkdownContent>
|
||||
<h3>{t('miniToc')}</h3>
|
||||
{miniTocItems && (
|
||||
<ActionList
|
||||
key={page.title}
|
||||
items={miniTocItems.map((items, i) => {
|
||||
return {
|
||||
key: page.title + i,
|
||||
text: page.title,
|
||||
renderItem: () => <ul>{renderTocItem(items, i)}</ul>,
|
||||
}
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ArticleGridLayout>
|
||||
</div>
|
||||
<MarkdownContent>
|
||||
{subcategories.map((subcategory, index) => (
|
||||
<div key={`restCategory-${index}`}>
|
||||
<div dangerouslySetInnerHTML={{ __html: descriptions[subcategory] }} />
|
||||
{restOperations[subcategory].map((operation, index) => (
|
||||
<RestOperation key={`restOperation-${index}`} operation={operation} index={index} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</MarkdownContent>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
)
|
||||
}
|
||||
|
||||
const Circle = ({ className, children }: { className?: string; children?: React.ReactNode }) => {
|
||||
return (
|
||||
<div
|
||||
className={cx('circle d-flex flex-justify-center flex-items-center', className)}
|
||||
style={{ width: 24, height: 24 }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,26 +11,26 @@ export const ScrollButton = ({ className, ariaLabel }: ScrollButtonPropsT) => {
|
|||
const [show, setShow] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
// show scroll button only when view is scrolled down
|
||||
const onScroll = function () {
|
||||
const y = document.documentElement.scrollTop // get the height from page top
|
||||
if (y < 100) {
|
||||
setShow(false)
|
||||
} else if (y >= 100) {
|
||||
setShow(true)
|
||||
}
|
||||
}
|
||||
window.addEventListener('scroll', onScroll)
|
||||
// We cannot determine document.documentElement.scrollTop height because we set the height: 100vh and set overflow to auto to keep the header sticky
|
||||
// That means window.scrollTop height is always 0
|
||||
// Using IntersectionObserver we can detemine if the h1 header is in view or not. If not, we show the scroll to top button, if so, we hide it
|
||||
const observer = new IntersectionObserver(
|
||||
function (entries) {
|
||||
if (entries[0].isIntersecting === false) {
|
||||
setShow(true)
|
||||
} else {
|
||||
setShow(false)
|
||||
}
|
||||
},
|
||||
{ threshold: [0] }
|
||||
)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', onScroll)
|
||||
}
|
||||
observer.observe(document.getElementsByTagName('h1')[0])
|
||||
}, [])
|
||||
|
||||
const onClick = () => {
|
||||
window.scrollTo(0, 0)
|
||||
const topOfPage = document.getElementById('github-logo')
|
||||
if (topOfPage) topOfPage.focus()
|
||||
document?.getElementById('github-logo')?.focus()
|
||||
document?.getElementById('main-content')?.scrollIntoView()
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -78,6 +78,12 @@
|
|||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
/* Background colors
|
||||
------------------------------------------------------------------------------*/
|
||||
.background-transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Widths / Heights
|
||||
------------------------------------------------------------------------------*/
|
||||
.max-w-xs {
|
||||
|
|
Загрузка…
Ссылка в новой задаче