Skip to content
Open
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
10 changes: 7 additions & 3 deletions packages/dev/s2-docs/src/CodePlatter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import {ActionButton, ActionButtonGroup, Button, ButtonGroup, Content, createIcon, Dialog, DialogContainer, Heading, Link, Menu, MenuItem, MenuTrigger, Text, Tooltip, TooltipTrigger} from '@react-spectrum/s2';
import {ActionButton, ActionButtonGroup, Button, ButtonGroup, Content, createIcon, Dialog, DialogContainer, Heading, Link, Menu, MenuItem, MenuTrigger, Text, UNSTABLE_ToastQueue as ToastQueue, Tooltip, TooltipTrigger} from '@react-spectrum/s2';
import {CopyButton} from './CopyButton';
import {createCodeSandbox, getCodeSandboxFiles} from './CodeSandbox';
import {createStackBlitz} from './StackBlitz';
Expand Down Expand Up @@ -97,7 +97,9 @@ export function CodePlatter({children, shareUrl, files, type, registryUrl, showC
if (node instanceof HTMLHeadingElement && node.id) {
url.hash = '#' + node.id;
}
navigator.clipboard.writeText(url.toString());
navigator.clipboard.writeText(url.toString()).catch(() => {
ToastQueue.negative('Failed to copy link.');
});
}}>
<LinkIcon />
<Text slot="label">Copy link</Text>
Expand Down Expand Up @@ -265,7 +267,9 @@ function ShadcnDialog({registryUrl}) {
<Button
variant="accent"
onPress={() => {
navigator.clipboard.writeText(preRef.current!.textContent!);
navigator.clipboard.writeText(preRef.current!.textContent!).catch(() => {
ToastQueue.negative('Failed to copy command. Please try again.');
});
close();
}}>
Copy and close
Expand Down
4 changes: 2 additions & 2 deletions packages/dev/s2-docs/src/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import {ActionButton, Tooltip, TooltipTrigger} from '@react-spectrum/s2';
import {ActionButton, UNSTABLE_ToastQueue as ToastQueue, Tooltip, TooltipTrigger} from '@react-spectrum/s2';
import CheckmarkCircle from '@react-spectrum/s2/icons/CheckmarkCircle';
import Copy from '@react-spectrum/s2/icons/Copy';
import React, {useEffect, useRef, useState} from 'react';
Expand Down Expand Up @@ -44,7 +44,7 @@ export function CopyButton({text, getText, ariaLabel = 'Copy', tooltip = 'Copy',
setIsCopied(true);
timeout.current = setTimeout(() => setIsCopied(false), 2000);
}).catch(() => {
// noop
ToastQueue.negative('Failed to copy.');
});
};

Expand Down
2 changes: 0 additions & 2 deletions packages/dev/s2-docs/src/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import BetaApp from '@react-spectrum/s2/icons/BetaApp';
import {flushSync} from 'react-dom';
import {getLibraryFromPage, getLibraryIcon, getLibraryLabel} from './library';
import GithubLogo from './icons/GithubLogo';
import {MarkdownMenu} from './MarkdownMenu';
// @ts-ignore
import {PageProps} from '@parcel/rsc';
import React, {CSSProperties, useId, useState} from 'react';
Expand Down Expand Up @@ -115,7 +114,6 @@ export default function Header(props: PageProps) {
<BetaApp />
<Text>Beta Preview</Text>
</Badge>
<MarkdownMenu url={currentPage.url} />
<ActionButton aria-label="React Spectrum GitHub repo" size="L" isQuiet onPress={() => window.open('https://github.com/adobe/react-spectrum', '_blank', 'noopener,noreferrer')}>
<GithubLogo />
</ActionButton>
Expand Down
4 changes: 2 additions & 2 deletions packages/dev/s2-docs/src/IconSearchView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import {Autocomplete, GridLayout, ListBox, ListBoxItem, Size, useFilter, Virtualizer} from 'react-aria-components';
import CheckmarkCircle from '@react-spectrum/s2/icons/CheckmarkCircle';
import Close from '@react-spectrum/s2/icons/Close';
import {Content, Heading, IllustratedMessage, pressScale, SearchField, Skeleton, Text} from '@react-spectrum/s2';
import {Content, Heading, IllustratedMessage, pressScale, SearchField, Skeleton, Text, UNSTABLE_ToastQueue as ToastQueue} from '@react-spectrum/s2';
import {focusRing, iconStyle, style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {iconAliases} from './iconAliases.js';
// @ts-ignore
Expand Down Expand Up @@ -50,7 +50,7 @@ export function useCopyImport() {
setCopiedId(id);
timeout.current = setTimeout(() => setCopiedId(null), 2000);
}).catch(() => {
// noop
ToastQueue.negative('Failed to copy import statement.');
});
}, []);

Expand Down
4 changes: 2 additions & 2 deletions packages/dev/s2-docs/src/IllustrationCards.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import {Autocomplete, GridLayout, ListBox, ListBoxItem, Size, useFilter, Virtualizer} from 'react-aria-components';
import {Content, Heading, IllustratedMessage, pressScale, ProgressCircle, Radio, RadioGroup, SearchField, SegmentedControl, SegmentedControlItem, Text} from '@react-spectrum/s2';
import {Content, Heading, IllustratedMessage, pressScale, ProgressCircle, Radio, RadioGroup, SearchField, SegmentedControl, SegmentedControlItem, Text, UNSTABLE_ToastQueue as ToastQueue} from '@react-spectrum/s2';
import {focusRing, style} from '@react-spectrum/s2/style' with {type: 'macro'};
// @ts-ignore
import Gradient from '@react-spectrum/s2/icons/Gradient';
Expand Down Expand Up @@ -100,7 +100,7 @@ let handleCopyImport = (id: string, variant: string, gradientStyle: string) => {
navigator.clipboard.writeText(importText).then(() => {
// noop
}).catch(() => {
// noop
ToastQueue.negative('Failed to copy import statement.');
});
};

Expand Down
230 changes: 121 additions & 109 deletions packages/dev/s2-docs/src/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {ExampleList} from './ExampleList';
import {MobileOnPageNav, Nav, OnPageNav, SideNav, SideNavItem, SideNavLink} from '../src/Nav';
import type {Page, PageProps, TocNode} from '@parcel/rsc';
import {Nav, PendingPageProvider} from '../src/Nav';
import {OptimisticMobileToc, OptimisticToc} from './OptimisticToc';
import type {Page, PageProps} from '@parcel/rsc';
import React, {ReactElement} from 'react';
// @ts-ignore
import '../src/client';
Expand All @@ -9,11 +10,13 @@ import internationalizedFavicon from 'url:../assets/internationalized.ico';
// @ts-ignore
import reactAriaFavicon from 'url:../assets/react-aria.ico';
import './anatomy.css';
import './footer.css';
import ChevronRightIcon from '@react-spectrum/s2/icons/ChevronRight';
import {ClassAPI} from './ClassAPI';
import {Code} from './Code';
import {CodeBlock} from './CodeBlock';
import {CodePlatterProvider} from './CodePlatter';
import {Divider, Provider, UNSTABLE_ToastContainer as ToastContainer} from '@react-spectrum/s2';
import {ExampleSwitcher} from './ExampleSwitcher';
import {getLibraryFromPage, getLibraryFromUrl, getLibraryLabel} from './library';
import {getTextWidth} from './textWidth';
Expand All @@ -22,7 +25,7 @@ import Header from './Header';
import {iconStyle, style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {Link, TitleLink} from './Link';
import {MobileHeader} from './MobileHeader';
import {PickerItem, Provider} from '@react-spectrum/s2';
import {NavigationSuspense} from './NavigationSuspense';
import {PropTable} from './PropTable';
import {StateTable} from './StateTable';
import {TypeLink} from './types';
Expand Down Expand Up @@ -77,10 +80,6 @@ const subPageComponents = (previousPage?: Page) => ({
)
});

function anchorId(children) {
return children.replace(/\s/g, '-').replace(/[^a-zA-Z0-9-_]/g, '').toLowerCase();
}

const getTitle = (currentPage: Page): string => {
const explicitTitle = (currentPage as any).pageTitle || currentPage.exports?.pageTitle;
if (explicitTitle && explicitTitle !== currentPage.tableOfContents?.[0]?.title && explicitTitle !== currentPage.name) {
Expand Down Expand Up @@ -139,6 +138,36 @@ let articleStyles = style({
height: 'fit'
});

function Footer() {
const year = new Date().getFullYear();
return (
<footer
className={style({
marginTop: 32,
paddingY: 12
})}>
<Divider size="S" />
<ul
className={style({
display: 'flex',
justifyContent: 'end',
flexWrap: 'wrap',
paddingX: 12,
margin: 0,
marginTop: 16,
font: 'body-2xs',
listStyleType: 'none'
})}>
<li>Copyright © {year} Adobe. All rights reserved.</li>
<li><Link isQuiet href="//www.adobe.com/privacy.html" variant="secondary">Privacy</Link></li>
<li><Link isQuiet href="//www.adobe.com/legal/terms.html" variant="secondary">Terms of Use</Link></li>
<li><Link isQuiet href="//www.adobe.com/privacy/cookies.html" variant="secondary">Cookies</Link></li>
<li><Link isQuiet href="//www.adobe.com/privacy/ca-rights.html" variant="secondary">Do not sell my personal information</Link></li>
</ul>
</footer>
);
}

export function Layout(props: PageProps & {children: ReactElement<any>}) {
let {pages, currentPage, children} = props;
let hasToC = !currentPage.exports?.hideNav && currentPage.tableOfContents?.[0]?.children && currentPage.tableOfContents?.[0]?.children?.length > 0;
Expand Down Expand Up @@ -218,121 +247,104 @@ export function Layout(props: PageProps & {children: ReactElement<any>}) {
gap: {
default: 0,
lg: 12
},
minHeight: {
default: 'screen',
lg: 'auto'
}
})}>
<Header pages={pages} currentPage={currentPage} />
<MobileHeader
toc={(currentPage.tableOfContents?.[0]?.children?.length ?? 0) > 1 ? <MobileToc key="toc" toc={currentPage.tableOfContents ?? []} currentPage={currentPage} /> : null}
pages={pages}
currentPage={currentPage} />
<div className={style({display: 'flex', width: 'full'})}>
{currentPage.exports?.hideNav ? null : <Nav pages={pages} currentPage={currentPage} />}
<main
key={currentPage.url}
style={{borderBottomLeftRadius: 0, borderBottomRightRadius: 0}}
className={style({
isolation: 'isolate',
backgroundColor: 'base',
padding: {
default: 12,
lg: 40
},
borderRadius: {
default: 'none',
lg: 'xl'
},
boxShadow: {
lg: 'emphasized'
},
width: 'full',
boxSizing: 'border-box',
flexGrow: 1,
display: 'flex',
justifyContent: 'space-between',
position: 'relative',
height: {
lg: '[calc(100vh - 72px)]'
},
overflow: {
lg: 'auto'
}
})}>
<CodePlatterProvider library={getLibraryFromUrl(currentPage.url)}>
<article
className={articleStyles({isWithToC: hasToC})}>
{currentPage.exports?.version && <VersionBadge version={currentPage.exports.version} />}
{React.cloneElement(children, {
components: isSubpage ?
subPageComponents(parentPage) :
components,
pages
})}
</article>
</CodePlatterProvider>
{hasToC && <aside
<PendingPageProvider currentPage={currentPage}>
<MobileHeader
toc={<OptimisticMobileToc currentPage={currentPage} />}
pages={pages}
currentPage={currentPage} />
<div className={style({display: 'flex', width: 'full', flexGrow: {default: 1, lg: 0}})}>
{currentPage.exports?.hideNav ? null : <Nav pages={pages} currentPage={currentPage} />}
<main
key={currentPage.url}
style={{borderBottomLeftRadius: 0, borderBottomRightRadius: 0}}
className={style({
position: 'sticky',
top: 0,
height: 'fit',
maxHeight: 'screen',
overflow: 'auto',
paddingY: 32,
boxSizing: 'border-box',
display: {
isolation: 'isolate',
backgroundColor: 'base',
padding: {
default: 12,
lg: 40
},
borderRadius: {
default: 'none',
lg: 'block'
lg: 'xl'
},
boxShadow: {
lg: 'emphasized'
},
width: 'full',
boxSizing: 'border-box',
flexGrow: 1,
display: 'flex',
justifyContent: 'space-between',
position: 'relative',
height: {
lg: '[calc(100vh - 72px)]'
},
overflow: {
lg: 'auto'
}
})}>
<div className={style({font: 'title', minHeight: 32, paddingX: 12, display: 'flex', alignItems: 'center'})}>Contents</div>
<Toc toc={currentPage.tableOfContents?.[0]?.children ?? []} />
</aside>}
</main>
</div>
<div
className={style({
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
width: 'full'
})}>
<CodePlatterProvider library={getLibraryFromUrl(currentPage.url)}>
<NavigationSuspense pages={pages}>
<article
className={articleStyles({isWithToC: hasToC})}>
{currentPage.exports?.version && <VersionBadge version={currentPage.exports.version} />}
{React.cloneElement(children, {
components: isSubpage ?
subPageComponents(parentPage) :
components,
pages
})}
</article>
</NavigationSuspense>
</CodePlatterProvider>
<Footer />
</div>
{hasToC && <aside
className={style({
position: 'sticky',
top: 0,
height: {
default: 'fit',
lg: '[calc(100vh - 72px)]'
},
paddingY: 32,
paddingX: 4,
boxSizing: 'border-box',
width: 180,
flexShrink: 0,
display: {
default: 'none',
lg: 'flex'
},
flexDirection: 'column'
})}>
<OptimisticToc currentPage={currentPage} />
</aside>}
</main>
</div>
</PendingPageProvider>
</div>
<ToastContainer placement="bottom" />
</body>
</Provider>
);
}

function Toc({toc}) {
return (
<OnPageNav>
<SideNav>
{toc.map((c, i) => (
<SideNavItem key={i}>
<SideNavLink href={'#' + anchorId(c.title)}>{c.title}</SideNavLink>
{c.children.length > 0 && <Toc toc={c.children} />}
</SideNavItem>
))}
</SideNav>
</OnPageNav>
);
}

function MobileToc({toc, currentPage}) {
return (
<MobileOnPageNav currentPage={currentPage}>
{renderMobileToc(toc)}
</MobileOnPageNav>
);
}

function renderMobileToc(toc: TocNode[], seen = new Map()) {
return toc.map((c) => {
let href = c.level === 1 ? '#top' : '#' + anchorId(c.title);
if (seen.has(href)) {
seen.set(href, seen.get(href) + 1);
href += '-' + seen.get(href);
} else {
seen.set(href, 1);
}
return (<React.Fragment key={href}>
<PickerItem id={href} href={href}>{c.title}</PickerItem>
{c.children.length > 0 && renderMobileToc(c.children, seen)}
</React.Fragment>);
});
}

export function Time({date}: {date: string}) {
let dateObj = new Date(date);
return (
Expand Down
Loading