From 46b4e5cf3f96a4dea0d2d8d78f6e4a90023c04ec Mon Sep 17 00:00:00 2001 From: Vladimir Gorej Date: Tue, 12 Oct 2021 21:57:47 +0300 Subject: [PATCH] fix(highlight-code): handle mousewheel events properly SyntaxHighlighter component doesn't support ref. We had to use different approach to finds it's DOM Node using ref of the root Node of the render tree for HighlightCode component. Refs #7497 --- src/core/components/highlight-code.jsx | 131 ++++++++++++------------- 1 file changed, 61 insertions(+), 70 deletions(-) diff --git a/src/core/components/highlight-code.jsx b/src/core/components/highlight-code.jsx index f85694086dc..95ebc70f416 100644 --- a/src/core/components/highlight-code.jsx +++ b/src/core/components/highlight-code.jsx @@ -1,47 +1,40 @@ -import React, { Component } from "react" +import React, { useRef, useEffect } from "react" import PropTypes from "prop-types" +import cx from "classnames" import {SyntaxHighlighter, getStyle} from "core/syntax-highlighting" import get from "lodash/get" +import isFunction from "lodash/isFunction" import saveAs from "js-file-download" import { CopyToClipboard } from "react-copy-to-clipboard" -export default class HighlightCode extends Component { - static propTypes = { - value: PropTypes.string.isRequired, - getConfigs: PropTypes.func.isRequired, - className: PropTypes.string, - downloadable: PropTypes.bool, - fileName: PropTypes.string, - language: PropTypes.string, - canCopy: PropTypes.bool - } +const HighlightCode = ({value, fileName, className, downloadable, getConfigs, canCopy, language}) => { + const config = isFunction(getConfigs) ? getConfigs() : null + const canSyntaxHighlight = get(config, "syntaxHighlight.activated", true) + const highlighterStyle = getStyle(get(config, "syntaxHighlight.theme", "agate")) + const rootRef = useRef(null) - #childNodes + useEffect(() => { + const childNodes = Array + .from(rootRef.current.childNodes) + .filter(node => !!node.nodeType && node.classList.contains("microlight")) - downloadText = () => { - saveAs(this.props.value, this.props.fileName || "response.txt") - } + // eslint-disable-next-line no-use-before-define + childNodes.forEach(node => node.addEventListener("mousewheel", handlePreventYScrollingBeyondElement, { passive: false })) - handleRootRef = (node) => { - if (node === null) { - this.#childNodes = node - } else { - this.#childNodes = Array - .from(node.childNodes) - .filter(node => !!node.nodeType && node.classList.contains("microlight")) + return () => { + // eslint-disable-next-line no-use-before-define + childNodes.forEach(node => node.removeEventListener("mousewheel", handlePreventYScrollingBeyondElement)) } - } + }, [value, className, language]) - preventYScrollingBeyondElement = (e) => { - const target = e.target - - var deltaY = e.deltaY - var contentHeight = target.scrollHeight - var visibleHeight = target.offsetHeight - var scrollTop = target.scrollTop + const handleDownload = () => { + saveAs(value, fileName) + } + const handlePreventYScrollingBeyondElement = (e) => { + const { target, deltaY } = e + const { scrollHeight: contentHeight, offsetHeight: visibleHeight, scrollTop } = target const scrollOffset = visibleHeight + scrollTop - const isElementScrollable = contentHeight > visibleHeight const isScrollingPastTop = scrollTop === 0 && deltaY < 0 const isScrollingPastBottom = scrollOffset >= contentHeight && deltaY > 0 @@ -51,49 +44,47 @@ export default class HighlightCode extends Component { } } - UNSAFE_componentDidMount() { - [this.#syntaxHighlighter, this.#pre] - .map(element => element?.addEventListener("mousewheel", this.preventYScrollingBeyondElement, { passive: false })) - } - - UNSAFE_componentWillUnmount() { - [this.#syntaxHighlighter, this.#pre] - .map(element => element?.removeEventListener("mousewheel", this.preventYScrollingBeyondElement)) - } - - render () { - let { value, className, downloadable, getConfigs, canCopy, language } = this.props - - const config = getConfigs ? getConfigs() : {syntaxHighlight: {activated: true, theme: "agate"}} - - className = className || "" - - const codeBlock = get(config, "syntaxHighlight.activated") - ? + {!downloadable ? null : +
+ Download +
+ } + + {canCopy && ( +
+
+ )} + + {canSyntaxHighlight + ? + className={cx(className, "microlight")} + style={highlighterStyle} + > {value} - :
{value}
+ :
{value}
+ } - return ( -
- { !downloadable ? null : -
- Download -
- } +
+ ) +} - { !canCopy ? null : -
-
- } +HighlightCode.propTypes = { + value: PropTypes.string.isRequired, + getConfigs: PropTypes.func.isRequired, + className: PropTypes.string, + downloadable: PropTypes.bool, + fileName: PropTypes.string, + language: PropTypes.string, + canCopy: PropTypes.bool +} - { codeBlock } - - ) - } +HighlightCode.defaultProps = { + fileName: "response.txt" } + +export default HighlightCode