Якорные ссылки из Sanity в Gatsby


TL;DR версия — убедитесь, что вы реализовали onRouteUpdate и shouldUpdateScroll в gatsby-browser.js.

Что же такое якорная ссылка?

Якорные ссылки — это способ навигации в пределах одной и той же страницы в HTML. Проще всего представить их как оглавление или закладки на странице. Вы можете часто видеть якоря, используемые на страницах с разметкой, которые имеют теги заголовков в виде #. Теперь, чтобы эти обычные теги заголовков имели ссылку, они должны быть обернуты на переднем конце тегом link, подобно этому: <a href="#anchor"><h2>Ссылка на заголовок</h2></a>. Если вы посмотрите код на этой странице, вы даже увидите пример именно этого, поскольку блог написан в формате markdown и преобразован в HTML.

Sanity.io

Как это работает с Sanity.io

Sanity — это безголовая CMS, основанная на контенте. Вы пишете в текстовом редакторе, который создает переносимый текст. Поэтому, в отличие от markdown, вам не придется конвертировать элементы заголовка #, но вам придется сериализовать переносимый текст в нечто, что сможет понять Gatsby. Я не буду слишком подробно рассказывать о том, как создать сайт с помощью sanity.io, для этого есть несколько отличных руководств с помощью gatsby-source-sanity.

Расширение блога Sanity Gatsby

Пример блога Sanity.io Gatsby — это отличная отправная точка для быстрого запуска. Вы можете использовать его, а затем расширить функциональность, как вам захочется. В примере есть файл для постов, который выглядит примерно так же, как показано ниже. Нам важна строка <div>{_rawBody && <PortableText blocks={_rawBody} />}</div>.

import React from "react";
import PortableText from "./portableText";
import Card from "../Card";

export default (props) => {
  const { _rawBody, authors, categories } = props;
  return(
  <article className="flex flex-col w-full max-w-xl md:max-w-1xl md:max-w-2xl lg:max-w-3xl xl:max-w-6xl m-2 md:max-m-8 md:max-m-8 lg:max-m-8 xl:m-8">
    <div className="w-full">
    <Card {...props} />
    </div>
    <section className="markdown bg-white w-full rounded mt-4 p-4">
    <div>{_rawBody && <PortableText blocks={_rawBody} />}</div>
    <div>
      <aside>
        {categories && (
          <div>
            <h3>Categories</h3>
            <ul>
              {categories.map(category => (
                <li key={category._id}>{category.title}</li>
              ))}
            </ul>
          </div>
        )}
      </aside>
    </div>
    </section>
  </article>
  )}

Вход в полноэкранный режим Выход из полноэкранного режима

Это простой компонент react, который использует @sanity/block-content-to-react. Самое замечательное здесь то, что они разрешили использовать сериализаторы, и вы можете добавить множество настроек к любому блочному PortableText, который вы будете получать из graphql от Sanity.io.

import React from "react";
import clientConfig from "../../../client-config";
import BasePortableText from "@sanity/block-content-to-react";
import serializers from "../graphql/serializers";

const PortableText = ({ blocks }) => {
  return(
  <BasePortableText
    blocks={blocks}
    serializers={{...serializers}}
    {...clientConfig.sanity} />
  )
};

export default PortableText;

Вход в полноэкранный режим Выход из полноэкранного режима

Сериализаторы Sanity.io

Самое замечательное в сериализаторах то, что вы можете предоставить любой пользовательский React-компонент, который вы хотите использовать для обработки любых различных типов, поступающих от Sanity.io.

import React from "react";
import Figure from "./Figure";
import Code from "./Code";
import Img from "./Img";
import Block from "./Block";

const serializers = {
  types: {
    authorReference: ({ node }) => <span>{node.author.name}</span>,
    mainImage: Figure,
    code: Code,
    img: Img,
    block: Block
  },
};

export default serializers;

Вход в полноэкранный режим Выход из полноэкранного режима

Забавный и простой пример — img Хотя я мог бы добавить многое из этого в строку, я планирую использовать манипуляции с изображениями в cloudinary, чтобы применять к изображениям те эффекты, которые я хочу. Поэтому я добавил простой компонент Img, который принимает узел и выводит простой тег img с соответствующим текстом alt.

import React from "react";

export default ({ node }) => {
  const { asset } = node;
  return <img src={asset.src} alt={asset.alt} />;
};

Вход в полноэкранный режим Выход из полноэкранного режима

Теперь то же самое можно сделать для всех элементов типа block, которые появляются с помощью portableText. Поскольку мы используем потрясающий @sanity/block-content-to-react от Sanity.io, нам не нужно многого делать, но поскольку я опять же ленивый разработчик, я хочу, чтобы все эти заголовки волшебным образом имели связанные с ними теги якоря, но наш portableText выглядит примерно так: Чтобы это произошло, мы добавили сериализатор block: Block сериализатор выше, который Sanity.io имеет отличный пример, как настроить. Мой блок выглядит очень похоже, но он устанавливает теги Gatsby Link внутри каждого из этих заголовков (пока что h2 и h3).

import React from "react";
import { IoMdLink } from "react-icons/io/";
import { Link } from "gatsby";
const slugs = require(github-slugger)()

export default (props) => {
    slugs.reset();
    const style = props.node.style || 'normal';
    // If Heading Level add Anchor Link
    if (typeof location !== undefined && /^hd/.test(style)) {
      const level = style.replace(/[^d]/g, '')
      const slug = slugs.slug(props.children[0], false)
      switch (level) {
        case 'h3':
          return <h3 id={slug} className="flex">{props.children}<Link to={${location.pathname}#${slug}}><div className="py-1 pl-1 rotateIn"><IoMdLink /></div></Link></h3>
        default:
            return <h2 id={slug} className="flex">{props.children}<Link to={${location.pathname}#${slug}}><div className="py-1 pl-1 rotateIn"><IoMdLink /></div></Link></h2>
      }
    }

    return style === 'blockquote'
    ? <blockquote>{props.children}</blockquote>
    : <p>{props.children}</p>
};


Вход в полноэкранный режим Выход из полноэкранного режима

Добавление анкорных ссылок Gatsby

Несмотря на то, что у Кнута Мельвера есть отличное руководство под названием «Внутренние и внешние ссылки», в котором очень подробно описано, как добавлять ссылки в front end, я довольно ленивый разработчик и не хочу вручную выбирать и добавлять все мои якорные ссылки, поэтому я использовал вышеописанный метод. Этот же подход может быть реализован с помощью файлов уценки с помощью gatsby-remark-autolink-headers.Единственный недостаток, который я обнаружил, это прокрутка страницы до нужного места в Gatsby. Для этого Gatsby предоставляет несколько замечательных браузерных апи. Чтобы прокрутка происходила при первой загрузке страницы, нам нужно использовать onRouteUpdate, это позволит нам использовать местоположение и проверить наличие hash, которая является нашей якорной ссылкой. Я также реализовал shouldUpdateScroll, так как выбор внутренней ссылки не вызывает обновления маршрута, поэтому это было необходимо без refresh.gatsby-browser.js

/**
 * Implement Gatsby's Browser APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/browser-apis/
 */

// You can delete this file if you're not using it

exports.onRouteUpdate = ({location}) => {
  anchorScroll(location);
  return true;
};
exports.shouldUpdateScroll = ({
  routerProps: { location },
  getSavedScrollPosition
}) => {
  anchorScroll(location);
  return true;
}

function anchorScroll(location){
  // Check for location so build does not fail
  if (location && location.hash) {
    setTimeout(() => {
      // document.querySelector(${location.hash}).scrollIntoView({ behavior: 'smooth', block: 'start' });
      const item = document.querySelector(${location.hash}).offsetTop;
      const mainNavHeight = document.querySelector(nav).offsetHeight;
      window.scrollTo({top: item - mainNavHeight, left: 0, behavior: 'smooth'});
    }, 0);
  }
}

Вход в полноэкранный режим Выход из полноэкранного режима

Окончательный результат

Приятная плавная прокрутка экрана при обновлении и нажатии на внутреннюю ссылку.

Оцените статью
devanswers.ru
Добавить комментарий