Source: plot/core/PlotlyExtensions.js

import Plotly from 'plotly.js-dist';
import { PlotlySelect2dButton } from '../buttons/PlotlySelect2dButton.js';
import { PlotlyNotificationRedirection } from './utils/PlotlyNotificationRedirection.js';
import { PlotlyResetButton } from "../buttons/PlotlyResetButton.js";

export { PlotlyExtensions }

/**
 * Class that extend the default plotly functionality *after* creating the plot.
 */
class PlotlyExtensions {
    /**
     * Create a plotly extension object.
     * @param {HTMLElement} graphDiv - Plotly graphDiv configuration.
     * @param {Array} [data=[]] - Plotly data configuration.
     * @param {Object} [layout={}] - Plotly layout configuration.
     * @param {Object} [config={}] - Plotly config configuration.
     */
    constructor(graphDiv, data = [], layout = {}, config = {}) {
        this.graphDiv = graphDiv;
        this.data = data;
        this.layout = layout;
        this.config = config;
    }

    /**
     * Activate custom plotly extensions based on the configuration. 
     * @param {Object} settings - Dictionary containing configuration parameters.
     * @param {Boolean} [settings.styleResetButton=true] - Enable extending the default reset button styling.
     * @param {Boolean} [settings.setAutoRangeLimits=true] - Enable range limitation of the plot.
     * @param {Boolean|Object} [settings.redirectNotification=false] - Configure a custom notification function.
     * @param {messageCallback} [settings.redirectNotification.callback] - Callback for handling plotly's notification message, i.e., appending it to the DOM.
     * @param {Boolean} [settings.infoSelect2dButton=true] - Enable hint on how to use the select button for first time usage.
     */
    apply({ styleResetButton = true, setAutoRangeLimits = true, redirectNotification = false, infoSelect2dButton = true } = {}) {
        this.settings = { 'redirectNotification': redirectNotification };

        (styleResetButton) && this._styleResetButton();
        (setAutoRangeLimits) && this._setAutoRangeLimits();
        (Object.keys(redirectNotification).length != 0) && this._redirectNotification();
        (infoSelect2dButton) && this._infoSelect2dButton();
    }

    _styleResetButton() {
        const resetButton = new PlotlyResetButton(this.graphDiv);
        resetButton.registerEvents();
    }

    _infoSelect2dButton() {
        const selectButton = new PlotlySelect2dButton(this.graphDiv);
        selectButton.registerEvents();
    }

    _setAutoRangeLimits() {

        const axisTypes = ['xaxis', 'yaxis', 'zaxis'];
        const updateLayout = {};
        axisTypes.forEach((axisType) => {
            const axisRange = this.graphDiv._fullLayout[axisType]?.range;
            if (!axisRange)
                return

            const areDateObjectsOnAxis = isNaN(axisRange[0]);
            let minallowed;
            let maxallowed;
            if (areDateObjectsOnAxis) {
                minallowed = axisRange[0];
                maxallowed = axisRange[1];
            }
            else {
                minallowed = Math.min(axisRange[0], axisRange[1]);
                maxallowed = Math.max(axisRange[0], axisRange[1]);
            }
            updateLayout[`${axisType}.minallowed`] = minallowed;
            updateLayout[`${axisType}.maxallowed`] = maxallowed;


        });

        Plotly.relayout(this.graphDiv, updateLayout);
    }

    _redirectNotification() {
        if (this.settings['redirectNotification']?.['callback'])
            new PlotlyNotificationRedirection(this.settings['redirectNotification']['callback']);
    }
}