diff --git a/docs/core/api/useSuspense.md b/docs/core/api/useSuspense.md index 155ee1568fdd..bf5cdef28997 100644 --- a/docs/core/api/useSuspense.md +++ b/docs/core/api/useSuspense.md @@ -217,17 +217,17 @@ Cache policy is [Stale-While-Revalidate](https://tools.ietf.org/html/rfc5861) by ::: -:::react +::::react -::::info[React Native] +:::info[React Native] When using React Navigation, useSuspense() will trigger fetches on focus if the data is considered stale. -:::: - ::: +:::: + ## Types diff --git a/website/src/components/Demo/Demo.module.css b/website/src/components/Demo/Demo.module.css deleted file mode 100644 index 94c45a6cc4b7..000000000000 --- a/website/src/components/Demo/Demo.module.css +++ /dev/null @@ -1,59 +0,0 @@ -@media only screen and (min-device-width: 360px) and (max-device-width: 736px) { -} - -@media only screen and (min-width: 1024px) { -} - -@media only screen and (max-width: 1023px) { -} - -@media only screen and (min-width: 1400px) { -} - -@media only screen and (max-width: 1450px) { - .gridImageBlock { - display: flex; - flex-direction: column; - } -} - -@media only screen and (min-width: 1450px) { - .gridImageBlock { - display: flex; - flex-direction: row; - flex-wrap: wrap; - } -} - -.blockImage { - width: 60px; - height: 60px; - display: flex; - align-items: center; - justify-items: center; -} - -.blockImage img { - min-width: 60px; -} - -.blockImageFull { - display: flex; - align-items: center; - justify-items: center; - box-shadow: rgba(0, 0, 0, 0.45) 0px 10px 30px; - border-radius: 6px; - background: #1e1e1e; - margin-bottom: 40px; -} -.blockImageFull img, .blockImageFull video { - border-radius: 6px; -} -.blockImageFull video { - padding: 8px; -} - -.gridImageBlock { - justify-content: space-between; - margin-bottom: -40px; -} diff --git a/website/src/components/Grid.module.css b/website/src/components/Grid.module.css index 13961d81fca7..9941f1496367 100644 --- a/website/src/components/Grid.module.css +++ b/website/src/components/Grid.module.css @@ -1,22 +1,22 @@ .grid { display: grid; - grid-template-columns: minmax(min-content, 1fr) minmax(min-content, 1fr); + grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 15px; } .grid.wrap :global(.prism-code) { white-space: pre-wrap; - word-break: break-all; + overflow-wrap: anywhere; } @media only screen and (max-width: 768px) { .grid { - grid-template-columns: minmax(min-content, 1fr); + grid-template-columns: minmax(0, 1fr); } } @media only screen and (min-width: 997px) and (max-width: 1268px) { .grid { - grid-template-columns: minmax(min-content, 1fr); + grid-template-columns: minmax(0, 1fr); } } \ No newline at end of file diff --git a/website/src/components/Grid.tsx b/website/src/components/Grid.tsx index 0f46ee6d9761..e23fa8af6136 100644 --- a/website/src/components/Grid.tsx +++ b/website/src/components/Grid.tsx @@ -8,7 +8,6 @@ export default function Grid({ wrap = false, }: { children: ReactNode; - cols?: number; wrap?: boolean; }) { return ( diff --git a/website/src/components/HomepageFeatures.module.css b/website/src/components/HomepageFeatures.module.css index 8f8f01b36729..fdd9881e7965 100644 --- a/website/src/components/HomepageFeatures.module.css +++ b/website/src/components/HomepageFeatures.module.css @@ -1,12 +1,25 @@ .features { - display: flex; - align-items: center; margin: 3.2rem 0; padding: 0; width: 100%; } +.feature { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + .featureSvg { - height: 60px; - width: 60px; + height: 56px; + width: 56px; + margin-bottom: 0.75rem; + color: var(--ifm-color-primary); +} + +@media screen and (max-width: 996px) { + .feature { + margin-bottom: 2rem; + } } diff --git a/website/src/components/HomepageFeatures.tsx b/website/src/components/HomepageFeatures.tsx index d68234be891e..de3b2edfe193 100644 --- a/website/src/components/HomepageFeatures.tsx +++ b/website/src/components/HomepageFeatures.tsx @@ -1,6 +1,4 @@ import Link from '@docusaurus/Link'; -import useBaseUrl from '@docusaurus/useBaseUrl'; -import ThemedImage from '@theme/ThemedImage'; import clsx from 'clsx'; import React from 'react'; @@ -8,28 +6,14 @@ import styles from './HomepageFeatures.module.css'; import ChemicalCompositionSvg from '../../static/img/chemical-composition.svg'; import FastCarSvg from '../../static/img/fast-car.svg'; import GrowingBarChartSvg from '../../static/img/growing-bar-chart.svg'; +import TypeScriptSvg from '../../static/img/typescript-mono.svg'; -type SvgComponent = React.ComponentType>; - -interface BaseFeature { +interface FeatureItem { description: React.ReactNode; title: string; + Svg: React.ComponentType>; } -interface SvgFeature extends BaseFeature { - Svg: SvgComponent; - dark?: never; - light?: never; -} - -interface ThemedImageFeature extends BaseFeature { - dark: string; - light: string; - Svg?: never; -} - -type FeatureItem = SvgFeature | ThemedImageFeature; - const featureList: FeatureItem[] = [ { description: ( @@ -39,8 +23,7 @@ const featureList: FeatureItem[] = [ make it easy to avoid race conditions ), - light: '/img/typescript.svg', - dark: '/img/typescript.dark.svg', + Svg: TypeScriptSvg, title: 'Data Integrity', }, { @@ -82,57 +65,16 @@ const featureList: FeatureItem[] = [ }, ]; -function isSvgFeature(feature: FeatureItem): feature is SvgFeature { - return 'Svg' in feature && feature.Svg != null; -} - -function Feature(feature: FeatureItem) { - const { title, description } = feature; +function Feature({ title, description, Svg }: FeatureItem) { return ( -
-
- {isSvgFeature(feature) ? - - : - } -
-
-

{title}

-

{description}

-
+
+ +

{title}

+

{description}

); } -function SvgFeatureIcon({ Svg, title }: { Svg: SvgComponent; title: string }) { - return ; -} - -function ThemedFeatureIcon({ - light, - dark, - title, -}: { - light: string; - dark: string; - title: string; -}) { - return ( - - ); -} - export default function HomepageFeatures() { return (
diff --git a/website/src/components/StackBlitz.module.css b/website/src/components/StackBlitz.module.css new file mode 100644 index 000000000000..d130d0270a36 --- /dev/null +++ b/website/src/components/StackBlitz.module.css @@ -0,0 +1,63 @@ +.wrapper { + position: relative; + margin-bottom: var(--ifm-spacing-vertical); + border-radius: var(--ifm-code-border-radius); + overflow: hidden; + background: var(--monoco-code-background); + box-shadow: var(--ifm-global-shadow-lw); +} + +/* Sits behind the transparent placeholder iframe; the loaded embed paints over it */ +.loading { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: rgb(191, 199, 213); + font-size: 0.9rem; + letter-spacing: 0.02em; +} + +.frame { + position: relative; + display: block; + border: 0; + max-width: 100%; + max-height: calc(100vh - 64px); +} + +.card { + display: none; + flex-direction: column; + align-items: center; + gap: 0.75rem; + margin-bottom: var(--ifm-spacing-vertical); + padding: 1.5rem 1rem; + border-radius: var(--ifm-code-border-radius); + background: var(--ifm-color-emphasis-100); + border: 1px solid var(--ifm-color-emphasis-200); + text-align: center; +} + +.cardTitle { + margin: 0; + font-weight: var(--ifm-font-weight-semibold); +} + +.cardLinks { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem; +} + +/* Matches Docusaurus' mobile breakpoint (useWindowSize) */ +@media (max-width: 996px) { + .wrapper { + display: none; + } + .card { + display: flex; + } +} diff --git a/website/src/components/StackBlitz.tsx b/website/src/components/StackBlitz.tsx index e457e34c3112..9c4134772456 100644 --- a/website/src/components/StackBlitz.tsx +++ b/website/src/components/StackBlitz.tsx @@ -1,7 +1,7 @@ import Link from '@docusaurus/Link'; -import type { ReactElement } from 'react'; import { isBot } from './Playground/userAgent'; +import styles from './StackBlitz.module.css'; import { useHasIntersected } from './useHasIntersected'; export default function StackBlitz({ @@ -41,34 +41,55 @@ export default function StackBlitz({ ctl, initialpath, }).toString(); - const src = - app ? - `https://stackblitz.com/github/reactive/${repo}/tree/master/examples/${app}?${params}` - : `https://stackblitz.com/github/reactive/${repo}/tree/master?${params}`; + const projectPath = + app ? `${repo}/tree/master/examples/${app}` : `${repo}/tree/master`; + const projectUrl = `https://stackblitz.com/github/reactive/${projectPath}`; const [frameRef, hasIntersected] = useHasIntersected(); - let embedElement: ReactElement; - if (!hasIntersected || isBot) { - embedElement = ( - - ); - } else { - embedElement = ( - - ); - } - + // Embedded IDEs are cramped on phones, so CSS swaps in a link-out card. + // A media query (not useWindowSize) keeps the SSR markup right on first paint; + // the hidden iframe never intersects, so it never loads on phones. return ( <> - {embedElement} +
+

+ {app ? `Explore the ${app} example` : 'Explore the example'} +

+
+ + Open in StackBlitz + + + View source + +
+
+
+ + +

More Demos diff --git a/website/src/components/useHasIntersected.tsx b/website/src/components/useHasIntersected.tsx index 236bd69c5598..fc191f9dead4 100644 --- a/website/src/components/useHasIntersected.tsx +++ b/website/src/components/useHasIntersected.tsx @@ -1,13 +1,12 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; export function useHasIntersected(options: Props = {}) { const { threshold = 0.1, root = null, rootMargin = '0%' } = options; - const ref = useRef(null); + // Callback ref (via state) so a node mounted after the first render is observed + const [node, ref] = useState(null); const [hasIntersected, setHasIntersected] = useState(false); useEffect(() => { - const node = ref.current; - if (!node || typeof IntersectionObserver !== 'function') { return; } @@ -30,7 +29,7 @@ export function useHasIntersected(options: Props = {}) { return () => { observer.disconnect(); }; - }, [threshold, root, rootMargin]); + }, [node, threshold, root, rootMargin]); return [ref, hasIntersected] as const; } diff --git a/website/src/css/root.css b/website/src/css/root.css index df91888ec535..14e0ced6514c 100644 --- a/website/src/css/root.css +++ b/website/src/css/root.css @@ -48,19 +48,10 @@ html[data-theme='light'] { --ifm-pre-background: var(--prism-background-color); } -.navbar .navbar__link { - font-family: var(--ifm-font-family-base); -} - .navbar .navbar__brand .navbar__title { font-family: var(--ifm-font-family-base); } -code, -pre { - font-family: var(-ifm-font-family-monospace); -} - html { font-family: var(--ifm-font-family-base); } @@ -70,3 +61,11 @@ table { width: 100%; font-variant-numeric: tabular-nums; } +/* MDX tables are wrapped (theme/MDXComponents) so wide ones scroll instead of widening the page */ +.table-scroll { + overflow-x: auto; + margin-bottom: var(--ifm-spacing-vertical); +} +.table-scroll > table { + margin-bottom: 0; +} diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 832e38c78ac9..16d82d7bbb4b 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -134,20 +134,20 @@ export default function Home() {

-
-
-
- -
-
-
+
+

A complete app

+

+ A GitHub issues and pull request browser on the live GitHub API, + built with REST resources and Suspense. +

+ +
diff --git a/website/src/pages/index.module.css b/website/src/pages/index.module.css index d723bddd7328..6745651c0330 100644 --- a/website/src/pages/index.module.css +++ b/website/src/pages/index.module.css @@ -8,19 +8,6 @@ text-align: center; position: relative; overflow: hidden; - /*margin-bottom: 10px;*/ - - /* background: linear-gradient( - transparent 212px, - var(--ifm-hero-fade-to-background-color) 100% - ), - url('../../static/img/rest_hooks_logo_grayscale.svg') - var(--ifm-hero-background-color) no-repeat; */ - background-position: 0, 100% calc(20% - 3rem); - - /* overlap */ - padding-bottom: 14.5rem; - margin-bottom: -12rem; } .logoWrapper { @@ -36,9 +23,7 @@ html[data-theme='light'] .logoWrapper { @media screen and (max-width: 996px) { .heroBanner { - padding: 2rem 0; - padding-bottom: 14rem; - margin-bottom: -13rem; + padding: 2rem 0 2.5rem; } } @@ -86,9 +71,7 @@ html[data-theme='light'] .buttons :global(.button.button--secondary) { margin-bottom: 20px; } .heroBanner { - padding: 1rem 0; - padding-bottom: 14rem; - + padding: 1rem 0 2rem; } } @@ -104,3 +87,13 @@ html[data-theme='light'] .buttons :global(.button.button--secondary) { z-index: 1; position: relative; } + +.sectionHeading { + text-align: center; + margin-bottom: 0.5rem; +} +.sectionLede { + text-align: center; + color: var(--ifm-color-emphasis-700); + margin-bottom: 1.5rem; +} diff --git a/website/src/theme/MDXComponents.js b/website/src/theme/MDXComponents.js index 90640295a062..dd09f1aa0f2c 100644 --- a/website/src/theme/MDXComponents.js +++ b/website/src/theme/MDXComponents.js @@ -1,8 +1,18 @@ // Import the original mapper import { RestEndpoint } from '@data-client/rest'; import MDXComponents from '@theme-original/MDXComponents'; +import React from 'react'; + +function Table(props) { + return ( +
+ + + ); +} export default { ...MDXComponents, RestEndpoint, + table: Table, }; diff --git a/website/static/img/typescript-mono.svg b/website/static/img/typescript-mono.svg new file mode 100644 index 000000000000..9f4165f52100 --- /dev/null +++ b/website/static/img/typescript-mono.svg @@ -0,0 +1 @@ + diff --git a/website/static/img/typescript.dark.svg b/website/static/img/typescript.dark.svg deleted file mode 100644 index b9810f4b6d79..000000000000 --- a/website/static/img/typescript.dark.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/website/static/img/typescript.svg b/website/static/img/typescript.svg deleted file mode 100644 index b9ece8995daa..000000000000 --- a/website/static/img/typescript.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - -