helpers/datetimeBuckets.js

import styles from "./styles";
import utils from "./utils";

/**
 * @typedef d3
 */

/**
 * Different types of Buckets.
 *
 * @private
 * @type {{UPPER_STEP_TICK_VALUES: string, LOWER_STEP_TICK_VALUES: string, MIDPOINT_TICK_VALUES: string}}
 */
const BUCKET_TYPES = {
    LOWER_STEP_TICK_VALUES: "lowerStepTickValues",
    MIDPOINT_TICK_VALUES: "midpointTickValues",
    UPPER_STEP_TICK_VALUES: "upperStepTickValues"
};
/**
 * Helper to create the d3 Axis ticks (X-Axis ticks) of different types and append into the canvas.
 *
 * @private
 * @param {object} gridSVG - d3 object of the grid
 * @param {object} axis - Axis scaled according to input parameters
 * @param {object} config - config object derived from input JSON
 * @param {Function} createVGridHandler - Call back function to create grid.
 * @returns {undefined} - returns nothing.
 */
const createDatetimeBuckets = (gridSVG, axis, config, createVGridHandler) => {
    if (
        utils.notEmpty(config.axis.x.ticks.values) &&
        utils.isEmpty(config.axis.x.ticks.lowerStepTickValues)
    ) {
        createVGridForDatetimeBuckets(
            gridSVG,
            axis,
            config.axis.x.ticks.values,
            config.axis.x.orientation,
            config.height,
            config.settingsDictionary.transition,
            BUCKET_TYPES.LOWER_STEP_TICK_VALUES,
            createVGridHandler
        );
    } else if (utils.notEmpty(config.axis.x.ticks.lowerStepTickValues)) {
        createVGridForDatetimeBuckets(
            gridSVG,
            axis,
            config.axis.x.ticks.lowerStepTickValues,
            config.axis.x.orientation,
            config.height,
            config.settingsDictionary.transition,
            BUCKET_TYPES.LOWER_STEP_TICK_VALUES,
            createVGridHandler
        );
    }

    if (utils.notEmpty(config.axis.x.ticks.midpointTickValues)) {
        createVGridForDatetimeBuckets(
            gridSVG,
            axis,
            config.axis.x.ticks.midpointTickValues,
            config.axis.x.orientation,
            config.height,
            config.settingsDictionary.transition,
            BUCKET_TYPES.MIDPOINT_TICK_VALUES,
            createVGridHandler
        );
    }

    if (utils.notEmpty(config.axis.x.ticks.upperStepTickValues)) {
        createVGridForDatetimeBuckets(
            gridSVG,
            axis,
            config.axis.x.ticks.upperStepTickValues,
            config.axis.x.orientation,
            config.height,
            config.settingsDictionary.transition,
            BUCKET_TYPES.UPPER_STEP_TICK_VALUES,
            createVGridHandler
        );
    }
};

/**
 * Helper to translate d3 Axis ticks (X-Axis ticks) of different types and append into the canvas.
 *
 * @private
 * @param {d3.selection} canvasSVG - d3 selection node of canvas svg
 * @param {object} axis - Axis scaled according to input parameters
 * @param {object} config - config object derived from input JSON
 * @param {Function} translateVGridHandler - Call back function to translate grid.
 * @returns {undefined} - returns nothing.
 */
const translateDatetimeBuckets = (
    canvasSVG,
    axis,
    config,
    translateVGridHandler
) => {
    if (
        utils.notEmpty(config.axis.x.ticks.values) &&
        utils.isEmpty(config.axis.x.ticks.lowerStepTickValues)
    ) {
        translateVGridDatetimeBuckets(
            canvasSVG,
            axis,
            config.axis.x.ticks.values,
            config.axis.x.orientation,
            config.height,
            config.settingsDictionary.transition,
            BUCKET_TYPES.LOWER_STEP_TICK_VALUES,
            translateVGridHandler
        );
    } else if (utils.notEmpty(config.axis.x.ticks.lowerStepTickValues)) {
        translateVGridDatetimeBuckets(
            canvasSVG,
            axis,
            config.axis.x.ticks.lowerStepTickValues,
            config.axis.x.orientation,
            config.height,
            config.settingsDictionary.transition,
            BUCKET_TYPES.LOWER_STEP_TICK_VALUES,
            translateVGridHandler
        );
    }

    if (utils.notEmpty(config.axis.x.ticks.midpointTickValues)) {
        translateVGridDatetimeBuckets(
            canvasSVG,
            axis,
            config.axis.x.ticks.midpointTickValues,
            config.axis.x.orientation,
            config.height,
            config.settingsDictionary.transition,
            BUCKET_TYPES.MIDPOINT_TICK_VALUES,
            translateVGridHandler
        );
    }

    if (utils.notEmpty(config.axis.x.ticks.upperStepTickValues)) {
        translateVGridDatetimeBuckets(
            canvasSVG,
            axis,
            config.axis.x.ticks.upperStepTickValues,
            config.axis.x.orientation,
            config.height,
            config.settingsDictionary.transition,
            BUCKET_TYPES.UPPER_STEP_TICK_VALUES,
            translateVGridHandler
        );
    }
};

/**
 * Helper to create the d3 Axis ticks (X-Axis ticks) of different types based on if its gantt or not.
 *
 * @private
 * @param {object} gridSVG - d3 object of the grid
 * @param {object} axis - Axis scaled according to input parameters
 * @param {Array} values - List of all tick values.
 * @param {string} orientation - X axis orientation.
 * @param {string} height - height of the grid, based on input config.
 * @param {object} transition - gets transition based on pannig mode is enabled or not
 * @param {string} type - type of tick. Either lowerStepTick or midpointTick or upperStepTick.
 * @param {Function} createVGridHandler - Call back function to create grid.
 * @returns {undefined} - returns nothing.
 */
const createVGridForDatetimeBuckets = (
    gridSVG,
    axis,
    values,
    orientation,
    height,
    transition,
    type,
    createVGridHandler
) => {
    const config = createDatetimeBucketConfig(
        values,
        height,
        orientation,
        transition
    );
    const style = determineBucketStyle(type);
    createVGridHandler(gridSVG, axis, style, config);
};

/**
 * Helper to translate the d3 Axis ticks (X-Axis ticks) of different types based on if its gantt or not.
 *
 * @private
 * @param {d3.selection} canvasSVG - d3 selection node of canvas svg
 * @param {object} axis - Axis scaled according to input parameters
 * @param {Array} values - List of all tick values.
 * @param {string} orientation - X axis orientation.
 * @param {string} height - height of the grid, based on input config.
 * @param {object} transition - gets transition based on pannig mode is enabled or not
 * @param {string} type - type of tick. Either lowerStepTick or midpointTick or upperStepTick.
 * @param {Function} translateVGridHandler - Call back function to translate grid.
 * @returns {undefined} - returns nothing.
 */
const translateVGridDatetimeBuckets = (
    canvasSVG,
    axis,
    values,
    orientation,
    height,
    transition,
    type,
    translateVGridHandler
) => {
    const config = createDatetimeBucketConfig(
        values,
        height,
        orientation,
        transition
    );
    const style = determineBucketStyle(type);
    translateVGridHandler(canvasSVG, axis, style, config);
};

/**
 * Helper to create the config for dateTimeBuckets for creating and translating grids and axis.
 *
 * @private
 * @param {Array} values - list of all tick values.
 * @param {string} height - Y axis height.
 * @param {string} orientation - X axis orientation.
 * @param {object} transition - gets transition based on pannig mode is enabled or not
 * @returns {object} - config with tick values and height.
 */
const createDatetimeBucketConfig = (
    values,
    height,
    orientation,
    transition
) => ({
    axis: {
        x: {
            ticks: {
                values
            },
            orientation: `${orientation}`
        }
    },
    height: `${height}`,
    settingsDictionary: {
        transition
    }
});

/**
 * Helper function to determine the type.
 *
 * @private
 * @param {string} type - different types of tick.
 * @returns {string} styling of the type.
 */
const determineBucketStyle = (type) => {
    switch (type) {
        case BUCKET_TYPES.LOWER_STEP_TICK_VALUES:
            return styles.gridLowerStep;
        case BUCKET_TYPES.MIDPOINT_TICK_VALUES:
            return styles.gridMidpoint;
        case BUCKET_TYPES.UPPER_STEP_TICK_VALUES:
            return styles.gridUpperStep;
        default:
            return "";
    }
};

/**
 * Helper function to create config for axes when dateTimeBuckets are used.
 * We only display lowerStepTickValues/values and upperStepTickValues in the axis, since,
 * midpointTickValues can be inferred.
 *
 * @private
 * @param {object} dateTimeBuckets - all tick values.
 * @returns {Array} newStepvalues - tick values based on bucket input.
 */
const constructDatetimeBucketValues = (dateTimeBuckets) => {
    let newStepValues = [];
    if (utils.notEmpty(dateTimeBuckets.values)) {
        newStepValues = newStepValues.concat(dateTimeBuckets.values);
    }
    if (utils.notEmpty(dateTimeBuckets.upperStepTickValues)) {
        newStepValues = newStepValues.concat(
            dateTimeBuckets.upperStepTickValues
        );
    }

    return newStepValues;
};

/**
 * Helper function to determine if the graph config is not a DatetimeBucket
 *
 * @private
 * @param {object} ticks - ticks object derived from input JSON.
 * @returns {boolean} - True, if DatetimeBucket. False, if it is not a DatetimeBucket.
 */
const hasDatetimeBuckets = (ticks) => {
    if (utils.isEmpty(ticks)) {
        return false;
    }
    return (
        utils.notEmpty(ticks.midpointTickValues) ||
        utils.notEmpty(ticks.upperStepTickValues) ||
        utils.notEmpty(ticks.lowerStepTickValues)
    );
};

/**
 * Helper function to create Vertical grid
 *
 * @private
 * @param {object} gridSVG - d3 object of the grid
 * @param {object} axis - Axis scaled according to input parameters
 * @param {object} config - config object derived from input JSON * @param gridSVG
 * @param {Function} createVGridHandler - Call back function to create grid.
 * @returns {undefined} - returns nothing
 */
const createVGrid = (gridSVG, axis, config, createVGridHandler) => {
    if (hasDatetimeBuckets(config.axis.x.ticks)) {
        createDatetimeBuckets(gridSVG, axis, config, createVGridHandler);
    } else {
        createVGridHandler(gridSVG, axis, styles.gridV, config);
    }
};

/**
 * Helper function to update horizontal axes, if graph has datetime buckets
 *
 * @private
 * @param {object} scale - d3 scale taking into account the input parameters
 * @param {object} axis - Axis scaled according to input parameters
 * @param {object} config - config object derived from input JSON * @param gridSVG
 * @param {Function} prepareXAxisHandler - Callback function to prepare the axis with new tick values
 * @param {string} [orientation] - Axis orientation
 * @returns {undefined} - returns nothing
 */
const prepareHAxis = (
    scale,
    axis,
    config,
    prepareXAxisHandler,
    orientation = config.axis.x.orientation
) => {
    if (hasDatetimeBuckets(config.axis.x.ticks)) {
        const values = config.axis.x.ticks.lowerStepTickValues
            ? config.axis.x.ticks.lowerStepTickValues
            : config.axis.x.ticks.values;
        const datetimeBuckets = {
            values,
            upperStepTickValues: config.axis.x.ticks.upperStepTickValues
        };
        axis.x = prepareXAxisHandler(
            scale,
            constructDatetimeBucketValues(datetimeBuckets),
            config,
            orientation
        );
    }
};

/**
 * Helper function to translate Vertical grid
 *
 * @private
 * @param {d3.selection} canvasSVG - d3 selection node of canvas svg
 * @param {object} axis - Axis scaled according to input parameters
 * @param {object} config - config object derived from input JSON * @param gridSVG
 * @param {Function} translateVGridHandler - Call back function to translate grid.
 * @returns {undefined} - returns nothing * @param axis
 */
const translateVGrid = (canvasSVG, axis, config, translateVGridHandler) => {
    if (hasDatetimeBuckets(config.axis.x.ticks)) {
        translateDatetimeBuckets(
            canvasSVG,
            axis,
            config,
            translateVGridHandler
        );
    } else {
        translateVGridHandler(canvasSVG, axis, styles.gridV, config);
    }
};

export { createVGrid, prepareHAxis, translateVGrid };