import classNames from 'classnames'; import { warn, } from 'colorful-logging'; import { IPassageRendererOwnProps, } from '../src/renderers/IPassageRendererOwnProps'; import { PassageContainer, } from '../src/components/PassageContainer'; import { ReactNodeWithoutNullOrUndefined, } from '../src/typeAliases/ReactNodeWithoutNullOrUndefined'; import { SkipToContentLinkDestination, } from '../src/components/SkipToContentLinkDestination'; import * as React from 'react'; import styles from '../passages/_global-styles/built-ins.less'; export class ScrollRenderer extends React.PureComponent { private elementBuffer: ReactNodeWithoutNullOrUndefined[] = []; private lastPassageTime: number; private unsubscribe: () => void; public readonly render = () => { const { context: { store: { getState }, }, } = this.props; const { history: { present: { passageName, passageTimeCounter, }, }, } = getState(); if (this.lastPassageTime === passageTimeCounter) { /* Do not add a new passage to the scroll if the passage time * counter has not progressed since the last render. */ return this.elementBuffer; } const ref = React.createRef(); this.elementBuffer.push(this.getPassageContainer(ref)); this.elementBuffer = this.maintainBuffer(this.elementBuffer); this.lastPassageTime = passageTimeCounter; /* Don't scroll if it's the first passage. */ if (this.elementBuffer.length > 1) { /* Don't fire the scroll event until rendering is complete. */ setTimeout(() => this.scrollToNewPassage(ref)); } return ( <> {this.elementBuffer.length === 1 ? : <> {this.elementBuffer.slice(0, this.elementBuffer.length - 1).map((child, index) => { const doRewind = () => this.doRewind(index); return (
{child}
); })} }
{this.elementBuffer[this.elementBuffer.length - 1]}
); }; private readonly getPassageContainer = (ref: React.RefObject) => { const { config, context: { footers, headers, passagesMap, store: { dispatch, getState, }, soundManager, }, passageFunctions, } = this.props; const { history: { present: { lastLinkTags, passageName, storyState, }, }, } = getState(); return ( ); }; public readonly componentDidMount = () => ( this.unsubscribe = this.props.context.store.subscribe(this.subscription) ); public readonly componentWillUnmount = () => this.unsubscribe(); private readonly maintainBuffer = ( buffer: readonly ReactNodeWithoutNullOrUndefined[], ) => buffer.slice(Math.max(buffer.length - 10, 0), buffer.length); private readonly subscription = () => { const { context: { store: { getState }, }, } = this.props; const { storyRequiresFullRerender } = getState(); if (storyRequiresFullRerender) { const ref = React.createRef(); this.elementBuffer = [ this.getPassageContainer(ref) ]; } }; private readonly scrollToNewPassage = (ref: React.RefObject) => { if (ref.current) { window.scrollTo({ top: ref.current.offsetTop }); ref.current.focus(); } else { warn('The ref has not been added and the passage cannot be scrolled.'); } }; private readonly doRewind = (index: number) => { const { passageFunctions: { rewind }, } = this.props; let counter = 0; rewind(() => (counter += 1) < index); }; }