<commit_before>// @flow
/**
 * This component, given a single DOMHighlight, draws highlight rectangles in
 * the same absolute position as the highlighted content, as computed via
 * `getClientRects`. On hover, renders a "Remove Highlight" tooltip that, when
 * clicked, fires a callback to remove this highlight.
 *
 * TODO(mdr): Many things can affect the correct positioning of highlighting,
 *     and this component does not attempt to anticipate them. If we start
 *     using this highlighting library on content with a more dynamic layout,
 *     we should add a hook to allow the parent to `forceUpdate` the
 *     `HighlightRenderer`.
 */
const React = require("react");
const {StyleSheet, css} = require("aphrodite");

const {getClientRectsForTextInRange, getRelativePosition, getRelativeRect} =
    require("./util.js");
const HighlightTooltip = require("./highlight-tooltip.jsx");

/* global i18n */

import type {DOMHighlight, Position, Rect, ZIndexes} from "./types.js";

type HighlightRendererProps = {
    // Whether this highlight is user-editable. If false, the highlight is
    // read-only.
    editable: boolean,

    // The DOMHighlight to render.
    highlight: DOMHighlight,

    // A unique key corresponding to the given `highlight`.
    highlightKey: string,

    // The mouse's current position, relative to the viewport.
    mouseClientPosition: ?Position,

    // This component's `offsetParent` element, which is the nearest ancestor
    // with `position: relative`. This will enable us to choose the correct
    // CSS coordinates to align highlights and tooltips with the target
    // content.
    offsetParent: Element,

    // A callback indicating that the user would like to remove this highlight.
    // Called with the highlight's key.
    onRemoveHighlight: (key: string) => mixed,

    // The z-indexes to use when rendering tooltips above content, and
    // highlights below content.
    zIndexes: ZIndexes,
};

type HighlightRendererState = {
    // The set of rectangles that cover this highlight's content, relative to
    // the offset parent. This cache is updated on mount and on changes to
    // the `highlight` and `offsetParent` props.
    //
    // We perform this caching because we need to access the rectangles every
    // time the user's mouse moves, in order to check the hover state, and
    // recomputing them on every mousemove seems like it could be expensive on
    // older devices (though tbf that's just a gut instinct, not the result of
    // testing on older devices).
    //
    // For most caching in highlighting, we take advatange of `PureComponent`,
    // and be mindful of the props we pass in. But this event happens on
    // mousemove, not on receive-props or set-state, so `PureComponent` doesn't
    // protect us from redundant work. We need to do it ourselves :/
    cachedHighlightRects: Rect[],

    // Whether the "Remove Highlight" tooltip is currently hovered. We don't
    // want to remove it while the user's mouse is over it!
    tooltipIsHovered: boolean,
};

class HighlightRenderer extends React.PureComponent {
    props: HighlightRendererProps
    state: HighlightRendererState = {
        cachedHighlightRects: this._computeRects(this.props),
        tooltipIsHovered: false,
    }

    componentWillReceiveProps(nextProps: HighlightRendererProps) {
        if (
            this.props.highlight !== nextProps.highlight ||
            this.props.offsetParent !== nextProps.offsetParent
        ) {
            this.setState({
                cachedHighlightRects: this._computeRects(nextProps),
            });
        }
    }

    /**
     * Compute the set of rectangles that cover the highlighted content, with
     * coordinates relative to the offset parent. That way, we can use them
     * for CSS positioning.
     */
    _computeRects(): Rect[] {
        const {highlight, offsetParent} = this.props;

        // Get the set of rectangles that covers the range's text, relative to
        // the offset parent.
        const clientRects = getClientRectsForTextInRange(highlight.domRange);
        const offsetParentRect = offsetParent.getBoundingClientRect();
        const relativeRects =
            clientRects.map(rect => getRelativeRect(rect, offsetParentRect));

        return relativeRects;
    }

    _handleRemoveHighlight = () => {
        this.props.onRemoveHighlight(this.props.highlightKey);
    }<commit_msg>[highlighting v2] minor bugfix<commit_after>// @flow
/**
 * This component, given a single DOMHighlight, draws highlight rectangles in
 * the same absolute position as the highlighted content, as computed via
 * `getClientRects`. On hover, renders a "Remove Highlight" tooltip that, when
 * clicked, fires a callback to remove this highlight.
 *
 * TODO(mdr): Many things can affect the correct positioning of highlighting,
 *     and this component does not attempt to anticipate them. If we start
 *     using this highlighting library on content with a more dynamic layout,
 *     we should add a hook to allow the parent to `forceUpdate` the
 *     `HighlightRenderer`.
 */
const React = require("react");
const {StyleSheet, css} = require("aphrodite");

const {getClientRectsForTextInRange, getRelativePosition, getRelativeRect} =
    require("./util.js");
const HighlightTooltip = require("./highlight-tooltip.jsx");

/* global i18n */

import type {DOMHighlight, Position, Rect, ZIndexes} from "./types.js";

type HighlightRendererProps = {
    // Whether this highlight is user-editable. If false, the highlight is
    // read-only.
    editable: boolean,

    // The DOMHighlight to render.
    highlight: DOMHighlight,

    // A unique key corresponding to the given `highlight`.
    highlightKey: string,

    // The mouse's current position, relative to the viewport.
    mouseClientPosition: ?Position,

    // This component's `offsetParent` element, which is the nearest ancestor
    // with `position: relative`. This will enable us to choose the correct
    // CSS coordinates to align highlights and tooltips with the target
    // content.
    offsetParent: Element,

    // A callback indicating that the user would like to remove this highlight.
    // Called with the highlight's key.
    onRemoveHighlight: (key: string) => mixed,

    // The z-indexes to use when rendering tooltips above content, and
    // highlights below content.
    zIndexes: ZIndexes,
};

type HighlightRendererState = {
    // The set of rectangles that cover this highlight's content, relative to
    // the offset parent. This cache is updated on mount and on changes to
    // the `highlight` and `offsetParent` props.
    //
    // We perform this caching because we need to access the rectangles every
    // time the user's mouse moves, in order to check the hover state, and
    // recomputing them on every mousemove seems like it could be expensive on
    // older devices (though tbf that's just a gut instinct, not the result of
    // testing on older devices).
    //
    // For most caching in highlighting, we take advatange of `PureComponent`,
    // and be mindful of the props we pass in. But this event happens on
    // mousemove, not on receive-props or set-state, so `PureComponent` doesn't
    // protect us from redundant work. We need to do it ourselves :/
    cachedHighlightRects: Rect[],

    // Whether the "Remove Highlight" tooltip is currently hovered. We don't
    // want to remove it while the user's mouse is over it!
    tooltipIsHovered: boolean,
};

class HighlightRenderer extends React.PureComponent {
    props: HighlightRendererProps
    state: HighlightRendererState = {
        cachedHighlightRects: this._computeRects(this.props),
        tooltipIsHovered: false,
    }

    componentWillReceiveProps(nextProps: HighlightRendererProps) {
        if (
            this.props.highlight !== nextProps.highlight ||
            this.props.offsetParent !== nextProps.offsetParent
        ) {
            this.setState({
                cachedHighlightRects: this._computeRects(nextProps),
            });
        }
    }

    /**
     * Compute the set of rectangles that cover the highlighted content, with
     * coordinates relative to the offset parent. That way, we can use them
     * for CSS positioning.
     */
    _computeRects(props: HighlightRendererProps): Rect[] {
        const {highlight, offsetParent} = props;

        // Get the set of rectangles that covers the range's text, relative to
        // the offset parent.
        const clientRects = getClientRectsForTextInRange(highlight.domRange);
        const offsetParentRect = offsetParent.getBoundingClientRect();
        const relativeRects =
            clientRects.map(rect => getRelativeRect(rect, offsetParentRect));

        return relativeRects;
    }

    _handleRemoveHighlight = () => {
        this.props.onRemoveHighlight(this.props.highlightKey);
    }
{'fraction_non_alphanumeric': 0.08302479524290363, 'fraction_numerical': 0.0005609783462358353, 'mean_word_length': 2.986583184257603, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3745190', 'n_tokens_mistral': 2371, 'n_tokens_neox': 2247, 'n_words': 924}