Scroll in React (пользовательский интерфейс Fabri c jumpLinks SideRail) - PullRequest
0 голосов
/ 31 января 2020

Необходимо реализовать активное изменение ссылки при прокрутке, используя React и Fabri c ui. Подошел к этому примеру https://github.com/OfficeDev/office-ui-fabric-react/blob/2a162cb9dcee4fb702ad3efc0b653478db4b012e/packages/example-app-base/src/components/SideRail/SideRail.tsx.

Пытаюсь использовать, но когда я нажимаю на ссылку, она фактически переходит на раздел, я бы предпочел использовать плавную прокрутку. Как я могу реализовать это в моем примере ниже?

  classNamesFunction,
  css,
  FocusZone,
  FocusZoneDirection,
  IProcessedStyleSet,
  Link,
  styled
} from "office-ui-fabric-react";
import * as React from "react";

import { getStyles } from "./SideRail.styles";
import {
  ISideRailLink,
  ISideRailProps,
  ISideRailStyleProps,
  ISideRailStyles
} from "./SideRail.types";

export interface ISideRailState {
  activeLink?: string;
}

const getClassNames = classNamesFunction<
  ISideRailStyleProps,
  ISideRailStyles
>();

class SideRailBase extends React.Component<ISideRailProps, ISideRailState> {
  public readonly state: ISideRailState = {};
  private _classNames: IProcessedStyleSet<ISideRailStyles>;
  private _observer: IntersectionObserver;

  public componentDidMount() {
    if (typeof IntersectionObserver !== "undefined") {
      const { observe, jumpLinks } = this.props;
      if (observe && jumpLinks) {
        this._observer = new IntersectionObserver(this._handleObserver, {
          threshold: [0.5]
        });

        jumpLinks.forEach((jumpLink: ISideRailLink) => {
          const element = document.getElementById(jumpLink.url);
          if (element) {
            this._observer.observe(element);
          }
        });
      }
    }
  }

  public componentWillUnmount() {
    if (this._observer) {
      this._observer.disconnect();
    }
  }

  render() {
    this._classNames = getClassNames(this.props.styles, {
      theme: this.props.theme
    });

    const jumpLinkList = this._renderJumpLinkList();

    return jumpLinkList ? (
      <FocusZone
        direction={FocusZoneDirection.vertical}
        className={this._classNames.root}
      >
        {jumpLinkList}
      </FocusZone>
    ) : null;
  }

  private _handleObserver = (entries: IntersectionObserverEntry[]) => {
    for (const entry of entries) {
      const { intersectionRatio, target } = entry;
      if (intersectionRatio > 0.5) {
        this.setState({
          activeLink: target.id
        });
        break;
      }
    }
  };

  private _renderJumpLinkList = (): JSX.Element | null => {
    const { activeLink } = this.state;
    const { jumpLinks } = this.props;
    const classNames = this._classNames;

    if (!jumpLinks || !jumpLinks.length) {
      return null;
    }
    const links = jumpLinks.map((jumpLink: ISideRailLink) => (
      <li
        key={jumpLink.url}
        className={css(classNames.linkWrapper, classNames.jumpLinkWrapper)}
      >
        <Link
          href={this._getJumpLinkUrl(jumpLink.url)}
          styles={{
            root: [
              classNames.jumpLink,
              activeLink === jumpLink.url && classNames.jumpLinkActive
            ]
          }}
        >
          {jumpLink.text}
        </Link>
      </li>
    ));
    return (
      <div className={css(classNames.section, classNames.jumpLinkSection)}>
        <ul className={classNames.links}>{links}</ul>
      </div>
    );
  };

  private _getJumpLinkUrl(anchor: string): string {
    // This makes sure that location hash changes don't append
    // eslint-disable-next-line no-restricted-globals
    return `${removeAnchorLink(location.hash)}#${anchor}`;
  }
}

/**
 * Remove the anchor link from a full page URL or a hash.
 * Preserves any route path specified in the URL.
 * Behavior with more than two # in the URL is unspecified.
 */
export const removeAnchorLink = (url: string): string => {
  // First group: most of the URL
  // Second group: optional last hash with only word or dash characters following (an anchor)
  const match = url.match(/^(.*?)(#[\w-]*)?$/);
  if (match) {
    return match[1];
  }
  return url;
};

export const SideRail: React.StatelessComponent<ISideRailProps> = styled<
  ISideRailProps,
  ISideRailStyleProps,
  ISideRailStyles
>(SideRailBase, getStyles, undefined, { scope: "SideRail" });
...