diff options
| author | 2021-08-14 00:58:00 +0000 | |
|---|---|---|
| committer | 2021-08-26 12:15:33 -0700 | |
| commit | b7ada11ddbabe4dd2f0798e140e5b280de4d6952 (patch) | |
| tree | c01ff3eee4f770fc108c19d126dce31d9cbe5e51 /examples/docs/src | |
| parent | 59cc62f7bd53dbcb6ad8051fa501b7b797614178 (diff) | |
| download | astro-b7ada11ddbabe4dd2f0798e140e5b280de4d6952.tar.gz astro-b7ada11ddbabe4dd2f0798e140e5b280de4d6952.tar.zst astro-b7ada11ddbabe4dd2f0798e140e5b280de4d6952.zip | |
WIP update examples/docs/
Diffstat (limited to 'examples/docs/src')
34 files changed, 1443 insertions, 336 deletions
| diff --git a/examples/docs/src/components/AvatarList.astro b/examples/docs/src/components/AvatarList.astro deleted file mode 100644 index aafcb371b..000000000 --- a/examples/docs/src/components/AvatarList.astro +++ /dev/null @@ -1,74 +0,0 @@ -<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! --> - -<ul class="avatar-list"> -  <li><a href="https://smolcss.dev/#smol-avatar-list"><img alt="Avatar 1" width="64" height="64" src='https://avataaars.io/?avatarStyle=Transparent&topType=LongHairBun&accessoriesType=Blank&hairColor=Auburn&facialHairType=BeardMedium&facialHairColor=Auburn&clotheType=ShirtCrewNeck&clotheColor=Blue01&eyeType=Side&eyebrowType=RaisedExcitedNatural&mouthType=Serious&skinColor=Tanned' /></a></li> -  <li><a href="https://smolcss.dev/#smol-avatar-list"><img alt="Avatar 2" width="64" height="64" src='https://avataaars.io/?avatarStyle=Transparent&topType=LongHairDreads&accessoriesType=Blank&hairColor=Brown&facialHairType=Blank&clotheType=ShirtScoopNeck&clotheColor=PastelGreen&eyeType=Default&eyebrowType=DefaultNatural&mouthType=Smile&skinColor=Tanned' /></a></li> -  <li><a href="https://smolcss.dev/#smol-avatar-list"><img alt="Avatar 3" width="64" height="64" src='https://avataaars.io/?avatarStyle=Transparent&topType=LongHairCurly&hairColor=BrownDark&facialHairType=Blank&clotheType=GraphicShirt&clotheColor=Pink&graphicType=Diamond&eyeType=Side&eyebrowType=Default&mouthType=Default&skinColor=Brown'/></a></li> -</ul> - -<style> -.avatar-list { -  --avatar-size: 2.5rem; -  --avatar-count: 3; - -  display: grid; -  list-style: none; -  /* Default to displaying most of the avatar to -  enable easier access on touch devices, ensuring -  the WCAG touch target size is met or exceeded */ -  grid-template-columns: repeat( -    var(--avatar-count), -    max(44px, calc(var(--avatar-size) / 1.15)) -  ); -  /* `padding` matches added visual dimensions of -  the `box-shadow` to help create a more accurate -  computed component size */ -  padding: 0.08em; -  font-size: var(--avatar-size); -} - -@media (any-hover: hover) and (any-pointer: fine) { -  .avatar-list { -    /* We create 1 extra cell to enable the computed  -    width to match the final visual width */ -    grid-template-columns: repeat( -      calc(var(--avatar-count) + 1), -      calc(var(--avatar-size) / 1.75) -    ); -  } -} - -.avatar-list li { -  width: var(--avatar-size); -  height: var(--avatar-size); -} - -.avatar-list li:hover ~ li a, -.avatar-list li:focus-within ~ li a { -  transform: translateX(33%); -} - -.avatar-list img, -.avatar-list a { -  display: block; -  border-radius: 50%; -} - -.avatar-list a { -  transition: transform 180ms ease-in-out; -} - -.avatar-list img { -  width: 100%; -  height: 100%; -  object-fit: cover; -  background-color: #fff; -  box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15); -} - -.avatar-list a:focus { -  outline: 2px solid transparent; -  /* Double-layer trick to work for dark and light backgrounds */ -  box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white; -} -</style> diff --git a/examples/docs/src/components/DocSidebar.tsx b/examples/docs/src/components/DocSidebar.tsx deleted file mode 100644 index 076d460cc..000000000 --- a/examples/docs/src/components/DocSidebar.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import type { FunctionalComponent } from 'preact'; -import { h } from 'preact'; -import { useState, useEffect, useRef } from 'preact/hooks'; -import EditOnGithub from './EditOnGithub'; - -const DocSidebar: FunctionalComponent<{ headers: any[]; editHref: string }> = ({ headers = [], editHref }) => { -  const itemOffsets = useRef([]); -  const [activeId, setActiveId] = useState<string>(undefined); - -  useEffect(() => { -    const getItemOffsets = () => { -      const titles = document.querySelectorAll('article :is(h2, h3, h4)'); -      itemOffsets.current = Array.from(titles).map((title) => ({ -        id: title.id, -        topOffset: title.getBoundingClientRect().top + window.scrollY, -      })); -    }; - -    const onScroll = () => { -      const itemIndex = itemOffsets.current.findIndex((item) => item.topOffset > window.scrollY + window.innerHeight / 3); -      if (itemIndex === 0) { -        setActiveId(undefined); -      } else if (itemIndex === -1) { -        setActiveId(itemOffsets.current[itemOffsets.current.length - 1].id); -      } else { -        setActiveId(itemOffsets.current[itemIndex - 1].id); -      } -    }; - -    getItemOffsets(); -    window.addEventListener('resize', getItemOffsets); -    window.addEventListener('scroll', onScroll); - -    return () => { -      window.removeEventListener('resize', getItemOffsets); -      window.removeEventListener('scroll', onScroll); -    }; -  }, []); - -  return ( -    <nav> -      <div> -        <h4>Contents</h4> -        <ul> -          {headers -            .filter(({ depth }) => depth > 1 && depth < 5) -            .map((header) => ( -              <li class={`header-link depth-${header.depth} ${activeId === header.slug ? 'active' : ''}`.trim()}> -                <a href={`#${header.slug}`}>{header.text}</a> -              </li> -            ))} -        </ul> -      </div> -      <div> -        <EditOnGithub href={editHref} /> -      </div> -    </nav> -  ); -}; - -export default DocSidebar; diff --git a/examples/docs/src/components/EditOnGithub.tsx b/examples/docs/src/components/EditOnGithub.tsx deleted file mode 100644 index f7478934f..000000000 --- a/examples/docs/src/components/EditOnGithub.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import type { FunctionalComponent } from 'preact'; -import { h } from 'preact'; - -const EditOnGithub: FunctionalComponent<{ href: string }> = ({ href }) => { -  return ( -    <a class="edit-on-github" href={href}> -      <svg -        preserveAspectRatio="xMidYMid meet" -        height="1em" -        width="1em" -        fill="currentColor" -        xmlns="http://www.w3.org/2000/svg" -        viewBox="0 0 438.549 438.549" -        stroke="none" -        class="icon-7f6730be--text-3f89f380" -      > -        <g> -          <path d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 0 1-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"></path> -        </g> -      </svg> -      <span>Edit on GitHub</span> -    </a> -  ); -}; - -export default EditOnGithub; diff --git a/examples/docs/src/components/Footer/AvatarList.astro b/examples/docs/src/components/Footer/AvatarList.astro new file mode 100644 index 000000000..589e296b9 --- /dev/null +++ b/examples/docs/src/components/Footer/AvatarList.astro @@ -0,0 +1,151 @@ +--- +// fetch all commits for just this page's path +const path = "docs/" + Astro.props.path; +const url = `https://api.github.com/repos/snowpackjs/astro/commits?path=${path}`; +const commitsURL = `https://github.com/snowpackjs/astro/commits/main/${path}`; + +async function getCommits(url) { +  try { +    const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN; +    if (!token) { +      throw new Error( +        'Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.' +      ); +    } + +    const auth = `Basic ${Buffer.from(token, "binary").toString("base64")}`; + +    const res = await fetch(url, { +      method: "GET", +      headers: { +        Authorization: auth, +        "User-Agent": "astro-docs/1.0", +      }, +    }); + +    const data = await res.json(); + +    if (!res.ok) { +      throw new Error( +        `Request to fetch commits failed. Reason: ${res.statusText} +       Message: ${data.message}` +      ); +    } + +    return data; +  } catch (e) { +    console.warn(`[error]  /src/components/AvatarList.astro  +    ${e?.message ?? e}`); +    return new Array(); +  } +} + +function removeDups(arr) { +  if (!arr) { +    return new Array(); +  } +  let map = new Map(); + +  for (let item of arr) { +    let author = item.author; +    // Deduplicate based on author.id +    map.set(author.id, { login: author.login, id: author.id }); +  } + +  return Array.from(map.values()); +} + +const data = await getCommits(url); +const unique = removeDups(data); +const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors +const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors + +--- +<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! --> +<div class="contributors"> +<ul class="avatar-list" style={`--avatar-count: ${recentContributors.length}`}> + +{recentContributors.map((item) => ( +      <li><a href={`https://github.com/${item.login}`}><img alt={`Contributor ${item.login}`} title={`Contributor ${item.login}`} width="64" height="64" src={`https://avatars.githubusercontent.com/u/${item.id}`}/></a></li> +       +))} +  </ul> +  {additionalContributors > 0 && <span><a href={commitsURL}>{`and ${additionalContributors} additional contributor${additionalContributors > 1 ? 's' : ''}.`}</a></span>} +  {unique.length === 0 && <a href={commitsURL}>Contributors</a>} +</div> + +<style> +.avatar-list { +  --avatar-size: 2.5rem; +  --avatar-count: 3; + +  display: grid; +  list-style: none; +  /* Default to displaying most of the avatar to +  enable easier access on touch devices, ensuring +  the WCAG touch target size is met or exceeded */ +  grid-template-columns: repeat( +    var(--avatar-count), +    max(44px, calc(var(--avatar-size) / 1.15)) +  ); +  /* `padding` matches added visual dimensions of +  the `box-shadow` to help create a more accurate +  computed component size */ +  padding: 0.08em; +  font-size: var(--avatar-size); +} + +@media (any-hover: hover) and (any-pointer: fine) { +  .avatar-list { +    /* We create 1 extra cell to enable the computed  +    width to match the final visual width */ +    grid-template-columns: repeat( +      calc(var(--avatar-count) + 1), +      calc(var(--avatar-size) / 1.75) +    ); +  } +} + +.avatar-list li { +  width: var(--avatar-size); +  height: var(--avatar-size); +} + +.avatar-list li:hover ~ li a, +.avatar-list li:focus-within ~ li a { +  transform: translateX(33%); +} + +.avatar-list img, +.avatar-list a { +  display: block; +  border-radius: 50%; +} + +.avatar-list a { +  transition: transform 180ms ease-in-out; +} + +.avatar-list img { +  width: 100%; +  height: 100%; +  object-fit: cover; +  background-color: #fff; +  box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15); +} + +.avatar-list a:focus { +  outline: 2px solid transparent; +  /* Double-layer trick to work for dark and light backgrounds */ +  box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white; +} + +.contributors { +  display: flex; +  align-items: center; +} + +.contributors > * + * { +  margin-left: .75rem; +} +</style> diff --git a/examples/docs/src/components/ArticleFooter.astro b/examples/docs/src/components/Footer/Footer.astro index 8078e2cc3..48de51054 100644 --- a/examples/docs/src/components/ArticleFooter.astro +++ b/examples/docs/src/components/Footer/Footer.astro @@ -1,9 +1,10 @@  ---  import AvatarList from './AvatarList.astro'; +const { path } = Astro.props;  ---  <footer> -  <AvatarList /> +  <AvatarList path={path} />  </footer>  <style> diff --git a/examples/docs/src/components/HeadCommon.astro b/examples/docs/src/components/HeadCommon.astro new file mode 100644 index 000000000..83045c0d1 --- /dev/null +++ b/examples/docs/src/components/HeadCommon.astro @@ -0,0 +1,40 @@ +<!-- Global Metadata --> +<meta name="viewport" content="width=device-width"> + +<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> +<link rel="alternate icon" type="image/x-icon" href="/favicon.ico" /> + +<link rel="sitemap" href="/sitemap.xml"/> + +<!-- Global CSS --> +<link rel="stylesheet" href="/theme.css" /> +<link rel="stylesheet" href="/code.css" /> +<link rel="stylesheet" href="/index.css" /> + +<!-- Preload Fonts --> +<link rel="preconnect" href="https://fonts.googleapis.com"> +<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> +<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital@0;1&display=swap" rel="stylesheet"> + +<!-- Scrollable a11y code helper --> +<script type="module" src="/make-scrollable-code-focusable.js" /> + +<!-- This is intentionally inlined to avoid FOUC --> +<script> +  const root = document.documentElement; +  const theme = localStorage.getItem('theme'); +  if (theme === 'dark' || (!theme) && window.matchMedia('(prefers-color-scheme: dark)').matches) { +    root.classList.add('theme-dark'); +  } else { +    root.classList.remove('theme-dark'); +  } +</script> + +<!-- Global site tag (gtag.js) - Google Analytics --> +<!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEL60V1WM9"></script> +<script> +  window.dataLayer = window.dataLayer || []; +  function gtag(){dataLayer.push(arguments);} +  gtag('js', new Date()); +  gtag('config', 'G-TEL60V1WM9'); +</script> -->
\ No newline at end of file diff --git a/examples/docs/src/components/HeadSEO.astro b/examples/docs/src/components/HeadSEO.astro new file mode 100644 index 000000000..5553eb2d0 --- /dev/null +++ b/examples/docs/src/components/HeadSEO.astro @@ -0,0 +1,40 @@ +--- +import {SITE, OPEN_GRAPH} from '../config.ts'; +export interface Props { +  content: any, +  site: any, +  canonicalURL: URL | string, +}; +const { content = {}, canonicalURL } = Astro.props; +const formattedContentTitle = content.title ? `${content.title} 🚀 ${SITE.title}` : SITE.title; +const imageSrc = content?.image?.src ?? OPEN_GRAPH.image.src; +const canonicalImageSrc = new URL(imageSrc, Astro.site); +const imageAlt = content?.image?.alt ?? OPEN_GRAPH.image.alt; +--- +<!-- Page Metadata --> +<link rel="canonical" href={canonicalURL}/> + +<!-- OpenGraph Tags --> +<meta property="og:title" content={formattedContentTitle}/> +<meta property="og:type" content="article"/> +<meta property="og:url" content={canonicalURL}/> +<meta property="og:locale" content={content.ogLocale ?? OPEN_GRAPH.locale}/> +<meta property="og:image" content={canonicalImageSrc}/> +<meta property="og:image:alt" content={imageAlt}/> +<meta property="og:description" content={content.description ? content.description : SITE.description}/> +<meta property="og:site_name" content={SITE.title}/> + +<!-- Twitter Tags --> +<meta name="twitter:card" content="summary_large_image"/> +<meta name="twitter:site" content={OPEN_GRAPH.twitter}/> +<meta name="twitter:title" content={formattedContentTitle}/> +<meta name="twitter:description" content={content.description ? content.description : SITE.description}/> +<meta name="twitter:image" content={canonicalImageSrc}/> +<meta name="twitter:image:alt" content={imageAlt}/> + +<!--  +  TODO: Add json+ld data, maybe https://schema.org/APIReference makes sense?  +  Docs: https://developers.google.com/search/docs/advanced/structured-data/intro-structured-data +  https://www.npmjs.com/package/schema-dts seems like a great resource for implementing this. +  Even better, there's a React component that integrates with `schema-dts`: https://github.com/google/react-schemaorg +--> diff --git a/examples/docs/src/components/Header/AstroLogo.astro b/examples/docs/src/components/Header/AstroLogo.astro new file mode 100644 index 000000000..ff1939ad9 --- /dev/null +++ b/examples/docs/src/components/Header/AstroLogo.astro @@ -0,0 +1,20 @@ +--- +const {size} = Astro.props; +--- +<svg class="logo" width={size} height={size} viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"> +    <style> +        #flame { +            /* fill: #ff5d01; */ +            fill: #3894ff; +        } +        #a { +            /* fill: #000014; */ +            fill: #3894ff; +        } +    </style> +    <title>Logo</title> +    <path id="a" fill-rule="evenodd" clip-rule="evenodd" +        d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z" /> +    <path id="flame" fill-rule="evenodd" clip-rule="evenodd" +        d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z" /> +</svg>
\ No newline at end of file diff --git a/examples/docs/src/components/Header/Header.astro b/examples/docs/src/components/Header/Header.astro new file mode 100644 index 000000000..cc54585b5 --- /dev/null +++ b/examples/docs/src/components/Header/Header.astro @@ -0,0 +1,158 @@ +--- +import SkipToContent from './SkipToContent.astro'; +import SidebarToggle from './SidebarToggle.tsx'; +import LanguageSelect from './LanguageSelect.jsx'; +import Search from "./Search.jsx"; +import { getLanguageFromURL } from '../util.ts'; + +const {currentPage} = Astro.props; +const lang = currentPage && getLanguageFromURL(currentPage); +--- +<style> +    header { +      z-index: 11; +      height: var(--theme-navbar-height); +      width: 100%; +      background-color: var(--theme-navbar-bg); +      display: flex; +      align-items: center; +      justify-content: center; +      overflow: hidden; +      position: sticky; +      top: 0; +    } + + +    .logo { +      display: flex; +      overflow: hidden; +      width: 30px; +      font-size: 1rem; +      flex-shrink: 0; +      font-weight: 600; +      line-height: 1; +      color: hsla(var(--color-base-white), 100%, 1); +      text-decoration: none; +      gap: 0.5em; +      z-index: -1; +    } + +    .logo a { +      padding: 0.5em 0.25em; +      margin: -0.5em -0.25em; +    } + +    .logo svg { +      height: 40px; +      width: auto; +      display: block; +      color: var(--theme-accent); +    } + +    .logo .hover { +      opacity: 0.0; +    } +    .logo a { +      transition: transform 180ms ease-out; +    } + +    .logo a:hover, +    .logo a:focus { +      outline: none; +      opacity: 1.0; +      transform: translateY(-2px); +    } + +    .logo h1 { +      font: inherit; +      color: inherit; +      margin: 0; +    } + +    .nav-wrapper { +      display: flex; +      align-items: center; +      justify-content: flex-end; +      gap: 1em; +      width: 100%; +      max-width: 82em; +      padding: 0 1rem; +    } + +    @media (min-width: 50em) { +        header { +            position: static; +            padding: 2rem 0rem 0 2rem; +        } +        .logo { +            width: auto; +            margin: 0; +            z-index: 0; +        } +        .menu-toggle { +            display: none; +        } +        .logo { +            width: auto; +        } +    } + +    /** Style Algolia */ +    :root { +      --docsearch-primary-color: var(--theme-accent); +      --docsearch-logo-color: var(--theme-text); +    } + +    .search-item { +      display: none; +      position: relative; +      z-index: 10; +      flex-grow: 1; +      padding-right: 0.7rem; +      display: flex; +      max-width: 200px; +    } +    :global(.search-item > *) { +      flex-grow: 1; +    } +    @media (min-width: 50em) { +      .search-item { +        max-width: 400px; +      } +    } +</style> +<header> +    <SkipToContent /> +    <nav class="nav-wrapper" title="Top Navigation"> +    <div class="menu-toggle"> +        <SidebarToggle client:idle/> +    </div> +    <div class="logo flex"> +        <a href="/"> +          <h1 class="sr-only">Astro</h1> +          <svg xmlns="http://www.w3.org/2000/svg" width="363" height="102" viewBox="0 0 363 102" fill="none"> +              <style> +              .text { +                  fill: var(--theme-text); +              } +              .hover { +                  fill: var(--theme-accent); +              } +              </style> +              <path class="text" fill-rule="evenodd" d="M55.07 14.216l16.81 54.865a72.6 72.6 0 00-20.765-6.984L39.808 24.135a1.475 1.475 0 00-2.827.005L25.81 62.078a72.598 72.598 0 00-20.859 6.995L21.847 14.2c.998-3.243 1.497-4.865 2.47-6.066a8 8 0 013.239-2.392c1.434-.576 3.13-.576 6.524-.576h8.751c3.398 0 5.097 0 6.532.577a8 8 0 013.241 2.397c.972 1.203 1.47 2.827 2.465 6.076z" clip-rule="evenodd"/> +              <path fill="#FF5D01" fill-rule="evenodd" d="M54.618 71.779c-2.863 2.432-8.578 4.091-15.161 4.091-8.08 0-14.852-2.499-16.649-5.86-.642 1.926-.786 4.13-.786 5.539 0 0-.423 6.915 4.418 11.725 0-2.498 2.037-4.522 4.551-4.522 4.309 0 4.304 3.734 4.3 6.764v.27c0 4.6 2.829 8.541 6.852 10.203a9.22 9.22 0 01-.938-4.064c0-4.386 2.592-6.02 5.604-7.917 2.396-1.51 5.06-3.188 6.894-6.554a12.297 12.297 0 001.502-5.905c0-1.314-.206-2.581-.587-3.77z" clip-rule="evenodd"/> +              <path class="text" d="M126.554 69c13.115 0 21.047-3.14 25.68-9.654 0 2.904.157 5.651.55 8.163h7.774c-.706-4.082-.863-6.75-.863-14.128V43.334c0-10.831-8.403-16.56-24.424-16.56-15.47 0-25.522 5.964-26.779 14.598h8.246c1.256-5.808 7.774-8.87 18.533-8.87 10.602 0 16.885 3.69 16.885 9.969v.785l-24.502 1.413c-9.974.549-13.665 1.962-16.492 4.003-2.67 1.962-4.162 5.023-4.162 8.555C107 64.683 114.696 69 126.554 69zm2.513-5.573c-9.109 0-14.135-2.119-14.135-6.357 0-4.553 3.141-6.593 14.214-7.3l23.01-1.412v1.805c0 8.241-9.66 13.264-23.089 13.264zM196.086 69c16.256 0 22.775-5.337 22.775-13.108 0-6.436-4.006-9.732-14.215-10.596l-19.083-1.49c-5.183-.393-8.088-1.884-8.088-5.102 0-4.082 4.476-6.201 14.135-6.201 10.995 0 16.727 2.198 20.497 7.064l6.361-3.061c-3.927-6.122-12.644-9.733-26.151-9.733-13.9 0-22.224 4.631-22.224 12.244 0 6.829 4.947 10.125 14.292 10.91l18.926 1.492c6.204.47 8.089 1.726 8.089 4.944 0 4.631-4.79 6.829-14.293 6.829-11.544 0-18.847-3.14-22.381-8.87l-6.204 3.376C173.312 64.918 181.715 69 196.086 69zM234.929 34.151v18.916c0 7.77 2.67 15.54 17.198 15.54 3.691 0 8.167-.706 10.131-1.57V60.68c-2.749.628-6.047 1.1-9.267 1.1-6.832 0-10.523-2.67-10.523-9.42V34.151h19.633v-5.887h-19.633V15l-7.539 3.061v10.204h-12.33v5.886h12.33zM280.823 28.265h-6.911v39.244h7.461V52.83c0-5.65 1.099-10.439 4.24-13.735 2.749-3.061 6.283-4.788 12.487-4.788 2.12 0 3.455.157 5.262.471v-7.22c-1.65-.393-3.063-.472-5.184-.472-8.402 0-15.078 4.945-17.355 12.558v-11.38zM334.807 69C351.534 69 363 60.523 363 47.887c0-12.637-11.466-21.114-28.193-21.114-16.727 0-28.193 8.477-28.193 21.114C306.614 60.523 318.08 69 334.807 69zm0-6.2c-12.329 0-20.261-5.809-20.261-14.913 0-9.105 7.932-14.913 20.261-14.913 12.251 0 20.261 5.808 20.261 14.913 0 9.104-8.01 14.912-20.261 14.912z"/> +          </svg> +        </a> +        <a href="/"> +          <h1 class="sr-only">Docs</h1> +          <svg xmlns="http://www.w3.org/2000/svg" width="226" height="102" viewBox="0 0 226 102" fill="none"> +              <path fill="currentColor" d="M25.805 68c14.688 0 24.883-8.41 24.883-21.14 0-12.786-9.62-19.756-24.653-19.756H0V68h25.805zm-14.17-33.005H24.25c8.352 0 14.17 4.09 14.17 12.039 0 8.236-5.3 13.075-14.113 13.075H11.635V34.995zM82.673 69.382c16.704 0 27.418-8.582 27.418-21.83 0-13.248-10.771-21.83-27.418-21.83-16.589 0-27.418 8.582-27.418 21.83 0 13.19 10.83 21.83 27.418 21.83zm0-8.64c-9.1 0-15.149-5.299-15.149-13.19 0-7.891 6.048-13.19 15.15-13.19 9.1 0 15.205 5.299 15.205 13.19 0 7.891-6.105 13.19-15.206 13.19zM141.497 69.382c13.306 0 22.637-5.299 25.978-14.572l-11.866-2.535c-1.67 5.415-6.393 8.295-13.709 8.295-9.216 0-15.033-5.127-15.033-13.018 0-8.006 5.702-13.018 14.918-13.018 7.43 0 12.154 3.053 13.709 8.64l12.038-2.13c-2.707-9.562-12.268-15.322-25.574-15.322-16.128 0-27.302 9.043-27.302 22.003 0 13.133 10.425 21.657 26.841 21.657zM194.94 69.382c14.745 0 23.212-5.01 23.212-14.054 0-7.603-4.665-10.944-15.955-12.096l-11.289-1.094c-5.242-.576-6.97-1.556-6.97-4.09 0-2.765 3.456-4.262 9.792-4.262 7.834 0 13.709 2.476 16.762 6.508l7.315-6.163c-5.069-5.702-13.133-8.41-23.501-8.41-13.997 0-21.888 4.781-21.888 12.903 0 7.546 4.781 11.232 14.803 12.326l12.557 1.383c4.896.518 6.624 1.555 6.624 4.09 0 3.225-3.456 4.723-10.886 4.723-8.352 0-14.688-3.226-18.087-8.007l-8.294 5.818c4.205 6.451 13.709 10.425 25.805 10.425z"/> +          </svg> +        </a> +    </div> +    <div style="flex-grow: 1;"></div> +    {lang && <LanguageSelect lang={lang} client:idle />} +    <div class="search-item"><Search client:idle /></div> +    </nav> +</header>
\ No newline at end of file diff --git a/examples/docs/src/components/Header/LanguageSelect.css b/examples/docs/src/components/Header/LanguageSelect.css new file mode 100644 index 000000000..4e878714b --- /dev/null +++ b/examples/docs/src/components/Header/LanguageSelect.css @@ -0,0 +1,47 @@ +.language-select { +  flex-grow: 1; +  width: 48px; +  box-sizing: border-box; +  margin: 0; +  padding: 0.33em 0.5em; +  overflow: visible; +  font-weight: 500; +  font-size: 1rem; +  font-family: inherit; +  line-height: inherit; +  background-color: var(--theme-bg); +  border-color: var(--theme-text-lighter); +  color: var(--theme-text-light); +  border-style: solid; +  border-width: 1px; +  border-radius: 0.25rem; +  outline: 0; +  cursor: pointer; +  transition-timing-function: ease-out; +  transition-duration: 0.2s; +  transition-property: border-color, color; +  -webkit-font-smoothing: antialiased; +  padding-left: 30px; +  padding-right: 1rem; +} +.language-select-wrapper .language-select:hover, +.language-select-wrapper .language-select:focus { +  color: var(--theme-text); +  border-color: var(--theme-text-light); +} +.language-select-wrapper { +  color: var(--theme-text-light); +  position: relative; +} +.language-select-wrapper > svg { +  position: absolute; +  top: 7px; +  left: 10px; +  pointer-events: none; +} + +@media (min-width: 50em) { +  .language-select { +    width: 100%; +  } +} diff --git a/examples/docs/src/components/Header/LanguageSelect.tsx b/examples/docs/src/components/Header/LanguageSelect.tsx new file mode 100644 index 000000000..cf325eedc --- /dev/null +++ b/examples/docs/src/components/Header/LanguageSelect.tsx @@ -0,0 +1,38 @@ +import type { FunctionalComponent } from 'preact'; +import { h } from 'preact'; +import './LanguageSelect.css'; +import { LANGUAGE_NAMES, langPathRegex } from '../../languages'; + +const LanguageSelect: FunctionalComponent<{ lang: string }> = ({ lang }) => { +  return ( +    <div class="language-select-wrapper"> +      <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88.6 77.3" height="1.2em" width="1.2em"> +        <path fill="currentColor" d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z" /> +        <path +          fill="currentColor" +          d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8          c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z" +        /> +      </svg> +      <select +        class="language-select" +        value={lang} +        onChange={(e) => { +          const newLang = e.target.value; +          let actualDest = window.location.pathname.replace(langPathRegex, '/'); +          if (actualDest == '/') actualDest = `/introduction`; +          window.location.pathname = '/' + newLang + actualDest; +        }} +      > +        {Object.keys(LANGUAGE_NAMES).map((key) => { +          return ( +            <option value={LANGUAGE_NAMES[key]}> +              <span>{key}</span> +            </option> +          ); +        })} +      </select> +    </div> +  ); +}; + +export default LanguageSelect; diff --git a/examples/docs/src/components/Header/Search.css b/examples/docs/src/components/Header/Search.css new file mode 100644 index 000000000..2056c2c8f --- /dev/null +++ b/examples/docs/src/components/Header/Search.css @@ -0,0 +1,76 @@ +/** Style Algolia */ +:root { +  --docsearch-primary-color: var(--theme-accent); +  --docsearch-logo-color: var(--theme-text); +} +.search-input { +  flex-grow: 1; +  box-sizing: border-box; +  width: 100%; +  margin: 0; +  padding: 0.33em 0.5em; +  overflow: visible; +  font-weight: 500; +  font-size: 1rem; +  font-family: inherit; +  line-height: inherit; +  background-color: var(--theme-divider); +  border-color: var(--theme-divider); +  color: var(--theme-text-light); +  border-style: solid; +  border-width: 1px; +  border-radius: 0.25rem; +  outline: 0; +  cursor: pointer; +  transition-timing-function: ease-out; +  transition-duration: 0.2s; +  transition-property: border-color, color; +  -webkit-font-smoothing: antialiased; +} +.search-input:hover, +.search-input:focus { +  color: var(--theme-text); +  border-color: var(--theme-text-light); +} +.search-input:hover::placeholder, +.search-input:focus::placeholder { +  color: var(--theme-text-light); +} +.search-input::placeholder { +  color: var(--theme-text-light); +} +.search-hint { +  position: absolute; +  top: 7px; +  right: 19px; +  padding: 3px 5px; +  display: none; +  display: none; +  align-items: center; +  justify-content: center; +  letter-spacing: 0.125em; +  font-size: 13px; +  font-family: var(--font-mono); +  pointer-events: none; +  border-color: var(--theme-text-lighter); +  color: var(--theme-text-light); +  border-style: solid; +  border-width: 1px; +  border-radius: 0.25rem; +  line-height: 14px; +} + +@media (min-width: 50em) { +  .search-hint { +    display: flex; +  } +} + +/* ------------------------------------------------------------ *\ +	DocSearch (Algolia) +\* ------------------------------------------------------------ */ + +.DocSearch-Modal .DocSearch-Hit a { +  box-shadow: none; +  border: 1px solid var(--theme-accent); +} diff --git a/examples/docs/src/components/Header/Search.tsx b/examples/docs/src/components/Header/Search.tsx new file mode 100644 index 000000000..d842e007f --- /dev/null +++ b/examples/docs/src/components/Header/Search.tsx @@ -0,0 +1,76 @@ +/* jsxImportSource: react */ +import { useState, useCallback, useRef } from 'react'; +import { createPortal } from 'react-dom'; +import { DocSearchModal, useDocSearchKeyboardEvents } from '@docsearch/react'; +import '@docsearch/css//dist/style.css'; +import './Search.css'; + +export default function Search() { +  const [isOpen, setIsOpen] = useState(false); +  const searchButtonRef = useRef(); +  const [initialQuery, setInitialQuery] = useState(null); + +  const onOpen = useCallback(() => { +    setIsOpen(true); +  }, [setIsOpen]); + +  const onClose = useCallback(() => { +    setIsOpen(false); +  }, [setIsOpen]); + +  const onInput = useCallback( +    (e) => { +      setIsOpen(true); +      setInitialQuery(e.key); +    }, +    [setIsOpen, setInitialQuery] +  ); + +  useDocSearchKeyboardEvents({ +    isOpen, +    onOpen, +    onClose, +    onInput, +    searchButtonRef, +  }); + +  return ( +    <> +      <button type="button" ref={searchButtonRef} onClick={onOpen} className="search-input"> +        <svg width="24" height="24" fill="none"> +          <path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> +        </svg> +        <span>Search</span> +        <span className="search-hint"> +          <span className="sr-only">Press </span> +          <kbd>/</kbd> +          <span className="sr-only"> to search</span> +        </span> +      </button> +      {isOpen && +        createPortal( +          <DocSearchModal +            initialQuery={initialQuery} +            initialScrollY={window.scrollY} +            onClose={onClose} +            indexName="astro" +            apiKey="0f387260ad74f9cbf4353facd29c919c" +            transformItems={(items) => { +              return items.map((item) => { +                // We transform the absolute URL into a relative URL to +                // work better on localhost, preview URLS. +                const a = document.createElement('a'); +                a.href = item.url; +                const hash = a.hash === '#overview' ? '' : a.hash; +                return { +                  ...item, +                  url: `${a.pathname}${hash}`, +                }; +              }); +            }} +          />, +          document.body +        )} +    </> +  ); +} diff --git a/examples/docs/src/components/Header/SidebarToggle.tsx b/examples/docs/src/components/Header/SidebarToggle.tsx new file mode 100644 index 000000000..97fece6b2 --- /dev/null +++ b/examples/docs/src/components/Header/SidebarToggle.tsx @@ -0,0 +1,27 @@ +import type { FunctionalComponent } from 'preact'; +import { h, Fragment } from 'preact'; +import { useState, useEffect } from 'preact/hooks'; + +const MenuToggle: FunctionalComponent = () => { +  const [sidebarShown, setSidebarShown] = useState(false); + +  useEffect(() => { +    const body = document.getElementsByTagName('body')[0]; +    if (sidebarShown) { +      body.classList.add('mobile-sidebar-toggle'); +    } else { +      body.classList.remove('mobile-sidebar-toggle'); +    } +  }, [sidebarShown]); + +  return ( +    <button type="button" aria-pressed={sidebarShown ? 'true' : 'false'} id="menu-toggle" onClick={() => setSidebarShown(!sidebarShown)}> +      <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24" stroke="currentColor"> +        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> +      </svg> +      <span className="sr-only">Toggle sidebar</span> +    </button> +  ); +}; + +export default MenuToggle; diff --git a/examples/docs/src/components/Header/SkipToContent.astro b/examples/docs/src/components/Header/SkipToContent.astro new file mode 100644 index 000000000..6df3a72ed --- /dev/null +++ b/examples/docs/src/components/Header/SkipToContent.astro @@ -0,0 +1,21 @@ +<style> +.skiplink, +.skiplink:focus, +.skiplink:focus-visible { +  position: absolute; +  padding: 0.25em; +  font-size: larger; +  top: 0; +  left: 0; +  right: 0; +  z-index: 9; +  display: block; +  text-align: center; +  background-color: var(--theme-text-accent); +  color: var(--theme-bg); +  border-radius: 0.25em; +  outline: var(--theme-bg) solid 1px; +  outline-offset: 0; +} +</style> +<a href="#article" class="sr-only skiplink"><span>Skip to Content</span></a> diff --git a/examples/docs/src/components/LeftSidebar/LeftSidebar.astro b/examples/docs/src/components/LeftSidebar/LeftSidebar.astro new file mode 100644 index 000000000..96bd36fba --- /dev/null +++ b/examples/docs/src/components/LeftSidebar/LeftSidebar.astro @@ -0,0 +1,109 @@ +--- +import { SIDEBAR } from '../../config.ts'; +import { getLanguageFromURL } from '../util.ts'; +const {currentPage} = Astro.props; +const currentPageMatch = currentPage.slice(1); +const langCode = getLanguageFromURL(currentPage); +// SIDEBAR is a flat array. Group it by sections to properly render.  +const sidebarSections = SIDEBAR[langCode].reduce((col, item) => { +  if (item.header) { +    col.push({...item, children: []}); +  } else { +    col[col.length-1].children.push(item); +  } +  return col; +}, []); + +--- + +<nav aria-labelledby="grid-left"> +  <ul class="nav-groups">   +    {sidebarSections.map(section => ( +      <li> +        <div class="nav-group"> +          <h2 class="nav-group-title">{section.text}</h2> +          <ul> +            {section.children.map(child => ( +              <li class="nav-link"><a href={`${Astro.site.pathname}${child.link}`} aria-current={`${currentPageMatch === child.link ? 'page' : 'false'}`}>{child.text}</a></li> +            ))} +          </ul> +        </div> +      </li> +    ))} +  </ul> +</nav> + +<script> +  window.addEventListener('DOMContentLoaded', (event) => { +    var target = document.querySelector('[aria-current="page"]'); +    if (target && (target.offsetTop > (window.innerHeight - 100))) { +      document.querySelector('.nav-groups').scrollTop = target.offsetTop; +    } +  }); +</script> + +<style> +  nav { +    width: 100%; +    margin-right: 1rem; +  } +  .nav-groups { +    height: 100%; +    padding: 2rem 0; +    overflow-x: visible; +    overflow-y: auto; +    max-height: 100vh; +  } + +  .nav-groups > li + li { +    margin-top: 2rem; +  } + +  .nav-groups > :first-child { +    padding-top: var(--doc-padding); +  } + +  .nav-groups > :last-child { +    padding-bottom: 2rem; +    margin-bottom: var(--theme-navbar-height); +  } + +  .nav-group-title { +    font-size: 1.0rem; +    font-weight: 700; +    padding: 0.1rem 1rem; +    text-transform: uppercase; +    margin-bottom: 0.5rem; +  } + +  .nav-link a { +    font-size: 1.0rem; +    margin: 1px; +    padding: 0.3rem 1rem; +    font: inherit; +    color: inherit; +    text-decoration: none; +    display: block; +  } +  .nav-link a:hover, +  .nav-link a:focus { +    background-color: var(--theme-bg-hover); +  } + +  .nav-link a[aria-current="page"] { +    color: var(--theme-text-accent); +    background-color: var(--theme-bg-accent); +    font-weight: 600; +  } + +  :global(:root.theme-dark) .nav-link a[aria-current="page"] { +    color: hsla(var(--color-base-white), 100%, 1); +  } + +  @media (min-width: 50em) { +    .nav-groups { +      padding: 0; +    } +  } + +</style> diff --git a/examples/docs/src/components/Note.astro b/examples/docs/src/components/Note.astro deleted file mode 100644 index c57ede3a0..000000000 --- a/examples/docs/src/components/Note.astro +++ /dev/null @@ -1,52 +0,0 @@ ---- -export interface Props { -  title: string; -  type?: 'tip' | 'warning' | 'error' -} -const { type = 'tip', title } = Astro.props; ---- - -<aside class={`note type-${type}`}> -  {title && <label>{title}</label>} -  <slot /> -</aside> - -<style> -  .note { -    --padding-block: 1rem; -    --padding-inline: 1.25rem; - -    display: flex; -    flex-direction: column; - -    padding: var(--padding-block) var(--padding-inline); -    margin-left: calc(var(--padding-inline) * -1); -    margin-right: calc(var(--padding-inline) * -1); -     -    background: var(--theme-bg-offset); -    border-left: calc(var(--padding-inline) / 2) solid var(--color); -    border-radius: 0; -  } - -  .note label { -    font-weight: 500; -    color: var(--color); -  } - -  /* .note :global(a) { -    color: var(--color); -  } */ - -  .note.type-tip { -    --color: var(--color-green); -    --color-rgb: var(--color-green-rgb); -  } -  .note.type-warning { -    --color: var(--color-yellow); -    --color-rgb: var(--color-yellow-rgb); -  } -  .note.type-error { -    --color: var(--color-red); -    --color-rgb: var(--color-red-rgb); -  } -</style> diff --git a/examples/docs/src/components/PageContent/PageContent.astro b/examples/docs/src/components/PageContent/PageContent.astro new file mode 100644 index 000000000..fd1e9d242 --- /dev/null +++ b/examples/docs/src/components/PageContent/PageContent.astro @@ -0,0 +1,41 @@ +--- +const {content, githubEditUrl} = Astro.props; +const title = content.title; +const headers = content.astro.headers; +import MoreMenu from '../RightSidebar/MoreMenu.astro'; +import TableOfContents from '../RightSidebar/TableOfContents.tsx'; +--- +<style> +      .content { +        padding: 0; +        max-width: 75ch; +        width: 100%; +        height: 100%; +        display: flex; +        flex-direction: column; +      } +      .content > section { +        margin-bottom: 4rem; +      } +      .block { +        display: block; +      } + +      @media (min-width: 50em) { +        .sm\:hidden { +          display: none; +        } +      } +</style> +<article id="article" class="content"> +    <section class="main-section"> +        <h1 class="content-title" id="overview">{title}</h1> +        <nav class="block sm:hidden"> +            <TableOfContents client:media="(max-width: 50em)" headers={headers}/> +        </nav> +        <slot /> +    </section> +    <nav class="block sm:hidden"> +        <MoreMenu editHref={githubEditUrl}/> +    </nav> +</article>
\ No newline at end of file diff --git a/examples/docs/src/components/RightSidebar/MoreMenu.astro b/examples/docs/src/components/RightSidebar/MoreMenu.astro new file mode 100644 index 000000000..6be2d86ee --- /dev/null +++ b/examples/docs/src/components/RightSidebar/MoreMenu.astro @@ -0,0 +1,68 @@ +--- +import ThemeToggleButton from './ThemeToggleButton.jsx'; +const {editHref} = Astro.props; +--- +<style> +  .edit-on-github { +    text-decoration: none; +    font: inherit; +    color: inherit; +    font-size: 1rem; +  } +</style> +<h2 class="heading">More</h2> +<ul> +  <li class={`header-link depth-2`}> +    <a class="edit-on-github" href={editHref} target="_blank"> +      <svg +        aria-hidden="true" +        focusable="false" +        data-prefix="fas" +        data-icon="pen" +        class="svg-inline--fa fa-pen fa-w-16" +        role="img" +        xmlns="http://www.w3.org/2000/svg" +        viewBox="0 0 512 512" +        height="1em" +        width="1em" +      > +        <path +          fill="currentColor" +          d="M290.74 93.24l128.02 128.02-277.99 277.99-114.14 12.6C11.35 513.54-1.56 500.62.14 485.34l12.7-114.22 277.9-277.88zm207.2-19.06l-60.11-60.11c-18.75-18.75-49.16-18.75-67.91 0l-56.55 56.55 128.02 128.02 56.55-56.55c18.75-18.76 18.75-49.16 0-67.91z" +        ></path> +      </svg> +      <span>Edit this page</span> +    </a> +  </li> +  <li class={`header-link depth-2`}> +    <a href="https://github.com/snowpackjs/astro/blob/main/CONTRIBUTING.md#translations" target="_blank"> +      <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88.6 77.3" height="1.24em" width="1.24em" style="margin: -2px;"> <path fill="currentColor" d="M61,24.6h7.9l18.7,51.6h-7.7l-5.4-15.5H54.3l-5.6,15.5h-7.2L61,24.6z M72.6,55l-8-22.8L56.3,55H72.6z" /> <path fill="currentColor" d="M53.6,60.6c-10-4-16-9-22-14c0,0,1.3,1.3,0,0c-6,5-20,13-20,13l-4-6c8-5,10-6,19-13c-2.1-1.9-12-13-13-19h8          c4,9,10,14,10,14c10-8,10-19,10-19h8c0,0-1,13-12,24l0,0c5,5,10,9,19,13L53.6,60.6z M1.6,16.6h56v-8h-23v-7h-9v7h-24V16.6z" /> </svg> +      <span>Translate this page</span> +    </a> +  </li> +  <li class={`header-link depth-2`}> +    <a href="https://astro.build/chat" target="_blank"> +      <svg +        aria-hidden="true" +        focusable="false" +        data-prefix="fas" +        data-icon="comment-alt" +        class="svg-inline--fa fa-comment-alt fa-w-16" +        role="img" +        xmlns="http://www.w3.org/2000/svg" +        viewBox="0 0 512 512" +        height="1em" +        width="1em" +      > +        <path +          fill="currentColor" +          d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 9.8 11.2 15.5 19.1 9.7L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64z" +        ></path> +      </svg> +      <span>Join our community</span> +    </a> +  </li> +</ul> +<div style="margin: 2rem 0; text-align: center;"> +  <ThemeToggleButton client:visible /> +</div> diff --git a/examples/docs/src/components/RightSidebar/RightSidebar.astro b/examples/docs/src/components/RightSidebar/RightSidebar.astro new file mode 100644 index 000000000..ed1dd37cc --- /dev/null +++ b/examples/docs/src/components/RightSidebar/RightSidebar.astro @@ -0,0 +1,25 @@ +--- +import TableOfContents from './TableOfContents.jsx'; +import MoreMenu from './MoreMenu.astro'; +const {content, githubEditUrl} = Astro.props; +const headers = content.astro.headers; +--- +<style> +  .sidebar-nav { +    width: 100%; +    position: sticky; +    top: 0; +  } +  .sidebar-nav-inner { +    height: 100%; +    padding: 0; +    padding-top: var(--doc-padding); +    overflow: auto; +  } +</style> +<nav class="sidebar-nav" aria-labelledby="grid-right"> +  <div class="sidebar-nav-inner"> +    <TableOfContents client:media="(min-width: 50em)" headers={headers} /> +    <MoreMenu editHref={githubEditUrl} /> +  </div> +</nav>
\ No newline at end of file diff --git a/examples/docs/src/components/RightSidebar/TableOfContents.tsx b/examples/docs/src/components/RightSidebar/TableOfContents.tsx new file mode 100644 index 000000000..d8ea998d4 --- /dev/null +++ b/examples/docs/src/components/RightSidebar/TableOfContents.tsx @@ -0,0 +1,45 @@ +import type { FunctionalComponent } from 'preact'; +import { h, Fragment } from 'preact'; +import { useState, useEffect, useRef } from 'preact/hooks'; + +const TableOfContents: FunctionalComponent<{ headers: any[] }> = ({ headers = [] }) => { +  const itemOffsets = useRef([]); +  const [activeId, setActiveId] = useState<string>(undefined); + +  useEffect(() => { +    const getItemOffsets = () => { +      const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)'); +      itemOffsets.current = Array.from(titles).map((title) => ({ +        id: title.id, +        topOffset: title.getBoundingClientRect().top + window.scrollY, +      })); +    }; + +    getItemOffsets(); +    window.addEventListener('resize', getItemOffsets); + +    return () => { +      window.removeEventListener('resize', getItemOffsets); +    }; +  }, []); + +  return ( +    <> +      <h2 class="heading">On this page</h2> +      <ul> +        <li class={`header-link depth-2 ${activeId === 'overview' ? 'active' : ''}`.trim()}> +          <a href="#overview">Overview</a> +        </li> +        {headers +          .filter(({ depth }) => depth > 1 && depth < 4) +          .map((header) => ( +            <li class={`header-link depth-${header.depth} ${activeId === header.slug ? 'active' : ''}`.trim()}> +              <a href={`#${header.slug}`}>{header.text}</a> +            </li> +          ))} +      </ul> +    </> +  ); +}; + +export default TableOfContents; diff --git a/examples/docs/src/components/RightSidebar/ThemeToggleButton.css b/examples/docs/src/components/RightSidebar/ThemeToggleButton.css new file mode 100644 index 000000000..7de231d1b --- /dev/null +++ b/examples/docs/src/components/RightSidebar/ThemeToggleButton.css @@ -0,0 +1,37 @@ +.theme-toggle { +  display: inline-flex; +  align-items: center; +  gap: 0.25em; +  padding: 0.33em 0.67em; +  border-radius: 99em; +  background-color: var(--theme-code-inline-bg); +} + +.theme-toggle > label:focus-within { +  outline: 2px solid transparent; +  box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white; +} + +.theme-toggle > label { +  color: var(--theme-code-inline-text); +  position: relative; +  display: flex; +  align-items: center; +  justify-content: center; +  opacity: 0.5; +} + +.theme-toggle .checked { +  color: var(--theme-accent); +  opacity: 1; +} + +input[name='theme-toggle'] { +  position: absolute; +  opacity: 0; +  top: 0; +  right: 0; +  bottom: 0; +  left: 0; +  z-index: -1; +} diff --git a/examples/docs/src/components/ThemeToggle.tsx b/examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx index 5a5061c15..75ea775f4 100644 --- a/examples/docs/src/components/ThemeToggle.tsx +++ b/examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx @@ -1,19 +1,13 @@  import type { FunctionalComponent } from 'preact';  import { h, Fragment } from 'preact';  import { useState, useEffect } from 'preact/hooks'; +import './ThemeToggleButton.css'; -const themes = ['system', 'light', 'dark']; +const themes = ['light', 'dark'];  const icons = [    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">      <path -      fill-rule="evenodd" -      d="M3 5a2 2 0 012-2h10a2 2 0 012 2v8a2 2 0 01-2 2h-2.22l.123.489.804.804A1 1 0 0113 18H7a1 1 0 01-.707-1.707l.804-.804L7.22 15H5a2 2 0 01-2-2V5zm5.771 7H5V5h10v7H8.771z" -      clip-rule="evenodd" -    /> -  </svg>, -  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor"> -    <path        fillRule="evenodd"        d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"        clipRule="evenodd" @@ -25,42 +19,48 @@ const icons = [  ];  const ThemeToggle: FunctionalComponent = () => { -  const [theme, setTheme] = useState(themes[0]); - -  useEffect(() => { -    const user = localStorage.getItem('theme'); -    if (!user) return; -    setTheme(user); -  }, []); +  const [theme, setTheme] = useState(() => { +    if (import.meta.env.SSR) { +      return undefined; +    } +    if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) { +      return localStorage.getItem('theme'); +    } +    if (window.matchMedia('(prefers-color-scheme: dark)').matches) { +      return 'dark'; +    } +    return 'light'; +  });    useEffect(() => {      const root = document.documentElement; -    if (theme === 'system') { -      localStorage.removeItem('theme'); -      if (window.matchMedia('(prefers-color-scheme: dark)').matches) { -        root.classList.add('theme-dark'); -      } else { -        root.classList.remove('theme-dark'); -      } +    if (theme === 'light') { +      root.classList.remove('theme-dark');      } else { -      localStorage.setItem('theme', theme); -      if (theme === 'light') { -        root.classList.remove('theme-dark'); -      } else { -        root.classList.add('theme-dark'); -      } +      root.classList.add('theme-dark');      }    }, [theme]);    return ( -    <div id="theme-toggle"> +    <div class="theme-toggle">        {themes.map((t, i) => {          const icon = icons[i];          const checked = t === theme;          return ( -          <label className={checked ? 'checked' : ''}> +          <label className={checked ? ' checked' : ''}>              {icon} -            <input type="radio" name="theme-toggle" checked={checked} value={t} title={`Use ${t} theme`} aria-label={`Use ${t} theme`} onChange={() => setTheme(t)} /> +            <input +              type="radio" +              name="theme-toggle" +              checked={checked} +              value={t} +              title={`Use ${t} theme`} +              aria-label={`Use ${t} theme`} +              onChange={() => { +                localStorage.setItem('theme', t); +                setTheme(t); +              }} +            />            </label>          );        })} diff --git a/examples/docs/src/components/SiteSidebar.astro b/examples/docs/src/components/SiteSidebar.astro deleted file mode 100644 index 0fbad0c83..000000000 --- a/examples/docs/src/components/SiteSidebar.astro +++ /dev/null @@ -1,20 +0,0 @@ ---- -import { sidebar } from '../config.ts'; ---- - -<nav> -  <ul class="nav-groups"> -    {sidebar.map(category => ( -      <li> -        <div class="nav-group"> -          <h4 class="nav-group-title"><a href={`${Astro.site}${category.link}`}>{category.text}</a></h4> -          <ul> -            {category.children.map(child => ( -              <li class="nav-link"><a href={`${Astro.site}${child.link}`}>{child.text}</a></li> -            ))} -          </ul> -        </div> -      </li> -    ))} -  </ul> -</nav> diff --git a/examples/docs/src/components/util.ts b/examples/docs/src/components/util.ts new file mode 100644 index 000000000..0ec91bce0 --- /dev/null +++ b/examples/docs/src/components/util.ts @@ -0,0 +1,4 @@ +export function getLanguageFromURL(pathname: string) { +  const langCodeMatch = pathname.match(/\/([a-z]{2}-?[A-Z]{0,2})\//); +  return langCodeMatch ? langCodeMatch[1] : 'en'; +} diff --git a/examples/docs/src/config.ts b/examples/docs/src/config.ts index 5ec22a02b..cf0d58ed5 100644 --- a/examples/docs/src/config.ts +++ b/examples/docs/src/config.ts @@ -1,10 +1,22 @@ -export const sidebar = [ -  { -    text: 'Introduction', -    link: '', // No leading slash needed, so this links to the homepage -    children: [ -      { text: 'Getting Started', link: 'getting-started' }, -      { text: 'Example', link: 'example' }, -    ], +export const SIDEBAR = { +  en: [ +    { text: 'Getting Started', header: true }, +    { text: 'Introduction', link: 'en/introduction' }, +    { text: 'Getting Started', link: 'en/getting-started' }, +    { text: 'Example', link: 'en/example' }, +  ], +}; + +export const SITE = { +  title: 'Astro Documentation', +  description: 'Build faster websites with less client-side Javascript.', +}; + +export const OPEN_GRAPH = { +  locale: 'en_US', +  image: { +    src: 'https://github.com/snowpackjs/astro/blob/main/assets/social/banner.png?raw=true', +    alt: 'astro logo on a starry expanse of space,' + ' with a purple saturn-like planet floating in the right foreground',    }, -]; +  twitter: 'astrodotbuild', +}; diff --git a/examples/docs/src/languages.ts b/examples/docs/src/languages.ts new file mode 100644 index 000000000..e56855631 --- /dev/null +++ b/examples/docs/src/languages.ts @@ -0,0 +1,19 @@ +export const LANGUAGE_NAMES = { +  English: 'en', +}; + +export const KNOWN_LANGUAGES = Object.values(LANGUAGE_NAMES); +export const langPathRegex = new RegExp(`\/(${KNOWN_LANGUAGES.join('|')})\/`); +export const getLanguageDetails = () => { +  // @ts-ignore +  let newLangWithRegion = (window.navigator.userLanguage || window.navigator.language || 'en-US').substr(0, 5); +  let newLang = newLangWithRegion.substr(0, 2); + +  let actualDest = window.location.pathname.replace(langPathRegex, '/'); +  return { +    newLangWithRegion, +    newLang, +    langPathRegex, +    actualDest, +  }; +}; diff --git a/examples/docs/src/layouts/MainLayout.astro b/examples/docs/src/layouts/MainLayout.astro new file mode 100644 index 000000000..04416316d --- /dev/null +++ b/examples/docs/src/layouts/MainLayout.astro @@ -0,0 +1,122 @@ +--- +import HeadCommon from "../components/HeadCommon.astro"; +import HeadSEO from "../components/HeadSEO.astro"; +import Header from '../components/Header/Header.astro'; +import Footer from '../components/Footer/Footer.astro'; +import PageContent from '../components/PageContent/PageContent.astro'; +import LeftSidebar from '../components/LeftSidebar/LeftSidebar.astro'; +import RightSidebar from '../components/RightSidebar/RightSidebar.astro'; +import { SITE } from "../config.ts"; + +const { content = {} } = Astro.props; +const currentPage = Astro.request.url.pathname; +const currentFile = `src/pages${currentPage.replace(/\/$/, "")}.md`; +const githubEditUrl = `https://github.com/snowpackjs/astro/blob/main/docs/${currentFile}`; +--- + +<html dir="{content.dir ?? 'ltr'}" lang="{content.lang ?? 'en-us'}" class="initial"> +  <head> +    <HeadCommon /> +    <HeadSEO {content} canonicalURL={Astro.request.canonicalURL} /> +    <title>{content.title ? `${content.title} 🚀 ${SITE.title}` : SITE.title}</title> +    <style> +      body { +        width: 100%; +        display: grid; +        grid-template-rows: var(--theme-navbar-height) 1fr; +        --gutter: 0.5rem; +        --doc-padding: 2rem; +      } +      .layout { +        display: grid; +        grid-auto-flow: column; +        grid-template-columns:  +          minmax(var(--gutter), 1fr)  +          minmax(0, var(--max-width))  +          minmax(var(--gutter), 1fr); +        overflow-x: hidden; +      } +      .layout :global(> *) { +        width: 100%; +        height: 100%; +      } +      .grid-sidebar { +        height: 100vh; +        position: sticky; +        top: 0; +        padding: 0; +      } +      #grid-left { +        position: fixed; +        background-color: var(--theme-bg); +        z-index: 10; +        display: none; +      } +      #grid-main { +        padding: var(--doc-padding) var(--gutter); +        grid-column: 2; +        display: flex; +        flex-direction: column; +        height: 100%; +      } +      #grid-right { +        display: none; +      }    +      :global(.mobile-sidebar-toggle) { +        overflow: hidden; +      } +      :global(.mobile-sidebar-toggle) #grid-left { +        display: block; +        top: 2rem; +      } +      @media (min-width: 50em) { +        .layout { +          overflow: initial; +          grid-template-columns:  +            20rem  +            minmax(0, var(--max-width)); +          gap: 1em; +        } +        #grid-left { +          display: flex; +          padding-left: 2rem; +          position: sticky; +          grid-column: 1; +        } +      } + +      @media (min-width: 72em) { +        .layout { +          grid-template-columns:  +            20rem  +            minmax(0, var(--max-width))  +            18rem; +          padding-left: 0; +          padding-right: 0; +          margin: 0 auto; +        } +        #grid-right { +          grid-column: 3; +          display: flex; +        } +      } +    </style> +  </head> + +  <body> +    <Header currentPage={currentPage} /> +    <main class="layout"> +      <aside id="grid-left" class="grid-sidebar" title="Site Navigation"> +        <LeftSidebar currentPage={currentPage} /> +      </aside> +      <div id="grid-main"> +        <PageContent content={content} githubEditUrl={githubEditUrl}> +          <slot /> +        </PageContent> +      </div> +      <aside id="grid-right" class="grid-sidebar" title="Table of Contents"> +        <RightSidebar content={content} githubEditUrl={githubEditUrl} /> +      </aside> +    </main> +  </body> +</html> diff --git a/examples/docs/src/pages/example.md b/examples/docs/src/pages/en/example.md index 4de84789b..a5deeaff9 100644 --- a/examples/docs/src/pages/example.md +++ b/examples/docs/src/pages/en/example.md @@ -1,6 +1,6 @@  ---  title: Markdown Example -layout: ../layouts/Main.astro +layout: ~/layouts/MainLayout.astro  ---  This is a fully-featured page, written in Markdown! @@ -17,7 +17,9 @@ Nam quam dolor, pellentesque sed odio euismod, feugiat tempus tellus. Quisque ar  ```markdown  --- -layout: ../layouts/Main.astro +title: Markdown Page! +lang: en +layout: ~/layouts/MainLayout.astro  ---  # Markdown example diff --git a/examples/docs/src/pages/en/getting-started.md b/examples/docs/src/pages/en/getting-started.md new file mode 100644 index 000000000..33494432c --- /dev/null +++ b/examples/docs/src/pages/en/getting-started.md @@ -0,0 +1,190 @@ +--- +title: Getting Started +layout: ~/layouts/MainLayout.astro +--- + +This template already provides your pages with a side bar navigation (on the left) for your pages, and a content navigation (on the right) for your sections. + +## Page navigation + +The page navigation, through the side bar on the left, needs to be manually updated. Open the `config.ts` file and you will find the following structure: + +```ts +export const SIDEBAR = { +  en: [ +    { text: 'Getting Started', header: true }, +    { text: 'Introduction', link: 'en/introduction' }, +    { text: 'Getting Started', link: 'en/getting-started' }, +    { text: 'Example', link: 'en/example' }, +  ], +  es: [ +    { text: 'Empezando', header: true }, +    { text: 'Introducción', link: 'es/introduction' }, +    { text: 'Empezando', link: 'es/getting-started' }, +    { text: 'Ejemplo', link: 'es/example' }, +  ], +  fr: [ +    { text: 'Commencer', header: true }, +    { text: 'Introduction', link: 'fr/introduction' }, +    { text: 'Commencer', link: 'fr/getting-started' }, +    { text: 'Exemple', link: 'fr/example' }, +  ], +}; +``` + +The sidebar supports many languages, and each language has items to display, and pages to link to, allowing for a truly native experience for international users. You can change this file to match the pages you want to display, the object with the `{ header: true, ... }` set to true will act as a section title and cannot contain a link. + +The page navigation is generated in the `src/components/LeftSidebar/LeftSidebar.astro`, so if you want to change the depth of elements displayed, styles, etc, that's the place to go. + +## Section navigation + +The section navigation, through the side bar on the right, is automatically generated by the `src/components/RightSidebar/RightSidebar.astro` file, it uses the meta-data from markdown files to generate the structure you see. + +By default only elements from depth 2 to 5 will be displayed, and at the moment doesn't work for `.astro files`. + +## Other Components + +### Footer + +You can edit your footer here `src/components/Footer/Footer.astro`, at the moment it is composed of a list of avatars. You can generate your own avatar [here](https://getavataaars.com/) and replace the ones from `src/components/Footer/AvatarList.astro`. + +### Theme + +The `src/components/RightSidebar/ThemeToggleButton.tsx` is only responsible for applying the theme, to change the theme colors see `public/theme.css` + +## Multiple Languages + +By default the Astro docs template encourages writing your docs in mutliple languages, it also encourages writing your docs in a specific file structure + +``` +📦pages + ┣ 📂en + ┃ ┣ 📜example.md + ┃ ┣ 📜getting-started.md + ┃ ┣ 📜index.astro + ┃ ┗ 📜introduction.md + ┣ 📂es + ┃ ┣ 📜example.md + ┃ ┣ 📜getting-started.md + ┃ ┣ 📜index.astro + ┃ ┗ 📜introduction.md + ┣ 📂fr + ┃ ┣ 📜example.md + ┃ ┣ 📜getting-started.md + ┃ ┣ 📜index.astro + ┃ ┗ 📜introduction.md + ┗ 📜index.astro +``` + +each folder within the `pages/` folder represents a language, to add new languages, you will need to create a new langauge folder, +add the langauges name to the `LANGUAGE_NAMES` variable in the [`languages.ts`](../../languages.ts) file, and add new sidebar links corrosponding to the new language. E.g. Adding Deutsch as a supported language + +1. Create the `de/` folder in the pages directory + +``` +📦pages + ┣ 📂en + ┃ ┣ 📜example.md + ┃ ┣ 📜getting-started.md + ┃ ┣ 📜index.astro + ┃ ┗ 📜introduction.md + ┣ 📂de + ┃ ┣ 📜example.md + ┃ ┣ 📜getting-started.md + ┃ ┣ 📜index.astro + ┃ ┗ 📜introduction.md + ┗ 📜index.astro +``` + +2. Add Deutsch to the `LANGUAGE_NAMES` variable in the [`languages.ts`](../../languages.ts) file + +```ts +// src/languages.ts +export const LANGUAGE_NAMES = { +  English: 'en', +  Deutsch: 'de', +}; + +// ... +``` + +3. Add Deutch as a localized language for the SIDEBAR + +```ts +// src/config.ts +export const SIDEBAR = { +  en: [ +    { text: 'Getting Started', header: true }, +    { text: 'Introduction', link: 'en/introduction' }, +    { text: 'Getting Started', link: 'en/getting-started' }, +    { text: 'Example', link: 'en/example' }, +  ], +  de: [ +    { text: 'Einstieg', header: true }, +    { text: 'Einführung', link: 'de/introduction' }, +    { text: 'Einstieg', link: 'de/getting-started' }, +    { text: 'Beispiel', link: 'de/example' }, +  ], +}; + +// ... +``` + +> _**Note**: make sure the sidebar links point to the proper language folder_ + +<!-- , but if you are unable to properly support multiple languages, you can disable multiple languages, you set the `DISABLE_MULTIPLE_LANGUAGES` variable in the [`config.ts`](../../config.ts) file to `true`, but you still need to change and tweak a couple more things. + +After settings `DISABLE_MULTIPLE_LANGUAGES` you can now move the pages from the language folder you wish to use, e.g. I speak english, so, I would delete every other folders and files in the [`pages/`](../) folder except for the [`en/`](./) folder, I would then move the files from the [`en/`](./) folder to the [`pages/`](../) folder, delete all `index.astro` files, and finally delete the [`en/`](./) folder. + +The file structure will look like this once you are done, + +``` +📦src + ┣ 📂components + ┃ ┣ ... + ┣ 📂layouts + ┃ ┗ 📜MainLayout.astro + ┣ 📂pages + ┃ ┣ 📜example.md + ┃ ┣ 📜getting-started.md + ┃ ┗ 📜introduction.md + ┣ 📜config.ts + ┗ 📜languages.ts +``` + +You will then need to rename `introductions.md` to `index.md`, and reorganize the `SIDEBAR` variable in the [`config.ts`](../../config.ts) file to resemble something like this (remember to change the links, since the `en/` folder has been deleted), + +```ts +export const SIDEBAR = [ +  // index.md is the homepage, so, you don't need to set a sidebar link +  { text: 'Introduction', header: true }, +  { text: 'Getting Started', link: 'getting-started' }, +  { text: 'Example', link: 'example' }, +] +``` + +and that's it. --> + +## Algolia DocSearch + +[Algolia](https://www.algolia.com/) offers [DocSearch](https://docsearch.algolia.com/), a _"State-of-the-art search for technical documentation"_. We use DocSearch for the Astro docs as it's a great documentation search engine, to make things setting up docs easier we built it into the docs template, you can setup DocSearch for your site by following these instructions, ... + +### 🛠 Configuration + +... + +## Documentation + +For more information on how to use Astro components, check the documentation pages: + +- [Quick Start](https://docs.astro.build/quick-start) +- [astro.config.mjs](https://docs.astro.build/reference/configuration-reference) +- [API](https://docs.astro.build/reference/api-reference) +- [Command Line Interface](https://docs.astro.build/reference/cli-reference) +- [Collections](https://docs.astro.build/core-concepts/collections) +- [Development Server](https://docs.astro.build/reference/dev/) +- [Markdown](https://docs.astro.build/guides/markdown-content) +- [Publishing Astro components](https://docs.astro.build/guides/publish-to-npm) +- [Renderers](https://docs.astro.build/reference/renderer-reference) +- [Styling](https://docs.astro.build/guides/styling) +- [.astro Syntax](https://docs.astro.build/core-concepts/astro-components) diff --git a/examples/docs/src/pages/en/index.astro b/examples/docs/src/pages/en/index.astro new file mode 100644 index 000000000..5731a3976 --- /dev/null +++ b/examples/docs/src/pages/en/index.astro @@ -0,0 +1,5 @@ +--- +import REDIRECT from "../index.astro"; +--- + +<REDIRECT />
\ No newline at end of file diff --git a/examples/docs/src/pages/index.md b/examples/docs/src/pages/en/introduction.md index cd5ce6454..7b3142f71 100644 --- a/examples/docs/src/pages/index.md +++ b/examples/docs/src/pages/en/introduction.md @@ -1,6 +1,6 @@  ---  title: Hello, Documentation! -layout: ../layouts/Main.astro +layout: ~/layouts/MainLayout.astro  ---  <img src="https://github.com/snowpackjs/astro/blob/main/assets/social/banner.png?raw=true" alt="Astro" width="638" height="320" > @@ -39,6 +39,7 @@ The default Astro project has the following `scripts` in the `/package.json` fil  ```json  {    "scripts": { +    "start": "astro dev",      "dev": "astro dev",      "build": "astro build",      "preview": "astro preview" diff --git a/examples/docs/src/pages/getting-started.md b/examples/docs/src/pages/getting-started.md deleted file mode 100644 index ab9c79617..000000000 --- a/examples/docs/src/pages/getting-started.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Getting Started -layout: ../layouts/Main.astro ---- - -This template already provides your pages with a side bar navigation (on the left) for your pages, and a content navigation (on the right) for your sections. - -## Page navigation - -The page navigation, through the side bar on the left, needs to be manually updated. Open the `config.ts` file and you will find the following structure: - -```ts -export const sidebar = [ -  { -    text: 'Introduction', -    link: '', // No leading slash needed, so this links to the homepage -    children: [ -      { text: 'Getting Started', link: 'getting-started' }, -      { text: 'Example', link: 'example' }, -    ], -  }, -]; -``` - -You can change this file to match the pages you want to display, the items within `children` can also have children elements, but only the first level and second levels will be displayed. - -The page navigation is generated in the `src/components/SiteSidebar.astro`, so if you want to change the depth of elements displayed, styles, etc, that's the place to go. - -## Section navigation - -The section navigation, through the side bar on the right, is automatically generated by the `src/components/DocSidebar.tsx` file, it uses the meta-data from markdown files to generate the structure you see. - -By default only elements from depth 2 to 5 will be displayed, and at the moment doesn't work for `.astro files`. - -## Other Components - -### Footer - -You can edit your footer here `src/components/ArticleFooter.astro`, at the moment it is composed of a list of avatars. You can generate your own avatar [here](https://getavataaars.com/) and replace the ones from `AvatarList.astro`. - -### Theme - -The `src/components/ThemeToggle.tsx` is only responsible for applying the theme, to change the theme colors see `public/theme.css` - -## Documentation - -For more information on how to use Astro components, check the documentation pages: - -- [Quick Start](https://docs.astro.build/quick-start) -- [astro.config.mjs](https://docs.astro.build/reference/configuration-reference) -- [API](https://docs.astro.build/reference/api-reference) -- [Command Line Interface](https://docs.astro.build/reference/cli-reference) -- [Collections](https://docs.astro.build/core-concepts/collections) -- [Development Server](https://docs.astro.build/reference/dev/) -- [Markdown](https://docs.astro.build/guides/markdown-content) -- [Publishing Astro components](https://docs.astro.build/guides/publish-to-npm) -- [Renderers](https://docs.astro.build/reference/renderer-reference) -- [Styling](https://docs.astro.build/guides/styling) -- [.astro Syntax](https://docs.astro.build/core-concepts/astro-components) diff --git a/examples/docs/src/pages/index.astro b/examples/docs/src/pages/index.astro new file mode 100644 index 000000000..f5a8fd318 --- /dev/null +++ b/examples/docs/src/pages/index.astro @@ -0,0 +1,24 @@ +--- +import Layout from '../layouts/MainLayout.astro'; +import { KNOWN_LANGUAGES } from "../languages"; +--- +<div id="known_languages" hidden>{KNOWN_LANGUAGES.join(",")}</div> +<script> +  // WIP: trigger a client-side redirect based on the browser language. +  // A vercel.json redirect is enforced in production, so no user should ever see this page. +  // Remove the vercel.json redirect when this is ready. +  const KNOWN_LANGUAGES = document.querySelector("#known_languages")?.textContent?.split(",") ?? ['bg', 'de','en','es','fi','nl','pt-br','zh-CN','zh-TW', 'fr']; +  let newLangWithRegion = (window.navigator.userLanguage || window.navigator.language || 'en-US').substr(0, 5); +  let newLang = newLangWithRegion.substr(0, 2); + +  let langPathRegex = new RegExp(`\/(${KNOWN_LANGUAGES.join("|")})\/`); +  let actualDest = window.location.pathname.replace(langPathRegex, "/"); +  if (actualDest == "/") actualDest = `/introduction`; +  if (KNOWN_LANGUAGES.includes(newLangWithRegion)) { +    window.location.pathname = '/' + newLangWithRegion + actualDest; +  } else if (KNOWN_LANGUAGES.includes(newLang)) { +    window.location.pathname = '/' + newLang + actualDest; +  } else { +    window.location.pathname = actualDest; +  } +</script> | 
