Source: plot/buttons/PlotlyResetButton.js

import { every } from "lodash";

import { readUTCDate } from "../utils";
export { PlotlyResetButton }

/**
 * Class extending plotly's reset button.
 */
class PlotlyResetButton {
    /**
     * Create the reset button extension.
     * @param {HTMLElement} graphDiv - Plotly graphDiv configuration.
     */
    constructor(graphDiv) {
        this.graphDiv = graphDiv;

        this.axisHomeState = {
            xaxis: this.graphDiv._fullLayout?.xaxis?.range,
            yaxis: this.graphDiv._fullLayout?.yaxis?.range,
            zaxis: this.graphDiv._fullLayout?.zaxis?.range
        };

        this.resetButton = this.graphDiv.querySelector('.modebar-btn[data-attr="zoom"][data-val="reset"]');
        this._deactivateButton();
    }

    /**
     * Include event listeners that react to layout changes and modify the reset button class name.
     */
    registerEvents() {
        this.resetButton.addEventListener("click", function (e) {
            this._deactivateButton();
        }.bind(this));

        this.graphDiv.on('plotly_doubleclick', function (e) {
            this._deactivateButton();
        }.bind(this));

        this.graphDiv.on('plotly_relayout', function (e) {
            const isHomeStateStatus = this._getButtonState(e);
            if (isHomeStateStatus == true)
                this._deactivateButton();
            else if (isHomeStateStatus == false)
                this._activateButton();

            else
                null;
        }.bind(this));

    }

    _activateButton() {
        this.resetButton.classList.add('vef-active-mode');
        this.resetButton.classList.remove('vef-inactive-mode');
    }

    _deactivateButton() {
        this.resetButton.classList.add('vef-inactive-mode');
        this.resetButton.classList.remove('vef-active-mode');
    }

    _getButtonState(e) {
        const axisTypes = ['xaxis', 'yaxis', 'zaxis'];
        const isHomeState = {};
        axisTypes.forEach((axisType) => {

            let shownAxisMinValue = e[`${axisType}.range[0]`];
            let shownAxisMaxValue = e[`${axisType}.range[1]`];

            let limitAxisMinValue = this.axisHomeState[axisType]?.at(0);
            let limitAxisMaxValue = this.axisHomeState[axisType]?.at(1);

            let allowedAxisMinValue = this.graphDiv._fullLayout[axisType]?.minallowed;
            let allowedAxisMaxValue = this.graphDiv._fullLayout[axisType]?.maxallowed;

            if ([shownAxisMinValue, shownAxisMaxValue, limitAxisMinValue, limitAxisMaxValue].some(item => item == undefined))
                return;


            const isAxisReversed = shownAxisMinValue > shownAxisMaxValue ? true : false;
            if (isAxisReversed)
                [shownAxisMinValue, shownAxisMaxValue] = [shownAxisMaxValue, shownAxisMinValue];
            const isLimitReversed = limitAxisMinValue > limitAxisMaxValue ? true : false;
            if (isLimitReversed)
                [limitAxisMinValue, limitAxisMaxValue] = [limitAxisMaxValue, limitAxisMinValue];


            const areDateObjectsOnAxis = isNaN(limitAxisMinValue);
            if (areDateObjectsOnAxis) {
                shownAxisMinValue = readUTCDate(shownAxisMinValue);
                shownAxisMaxValue = readUTCDate(shownAxisMaxValue);
                limitAxisMinValue = readUTCDate(limitAxisMinValue);
                limitAxisMaxValue = readUTCDate(limitAxisMaxValue);
                allowedAxisMinValue = readUTCDate(allowedAxisMinValue);
                allowedAxisMaxValue = readUTCDate(allowedAxisMaxValue);
            }

            const areDefinedAxisMinValues = !isNaN(allowedAxisMinValue);
            if (areDefinedAxisMinValues) {
                shownAxisMinValue = Math.max(shownAxisMinValue, allowedAxisMinValue);
                shownAxisMaxValue = Math.min(shownAxisMaxValue, allowedAxisMaxValue);
            }

            const allowedDeviation = 1 / 10000;
            const denominator = limitAxisMaxValue - limitAxisMinValue;
            const isHomeStateMinValue = Math.abs((limitAxisMinValue - shownAxisMinValue) / denominator) < allowedDeviation;
            const isHomeStateMaxValue = Math.abs((limitAxisMaxValue - shownAxisMaxValue) / denominator) < allowedDeviation;
            isHomeState[axisType] = isHomeStateMinValue && isHomeStateMaxValue;
        });

        const isHomeStateStatusUnknown = Object.keys(isHomeState).length === 0;
        const isHomeStateAllAxis = every(isHomeState, item => item);
        if (isHomeStateStatusUnknown)
            return null;
        return isHomeStateAllAxis;
    }

}