ui_elements_slider_ab.mjs

/**
Exports method to create a slider_ab element group.

@module /ui/elements/slider_ab
*/

/**
@function slider_ab

@description
The slider method creates a range slider element with two numeric input elements [a/b].

The params.value property must be between params.min and params.max params.

@param {Object} params Parameter for slider element.
@property {numeric} params.val_a Parameter value for a input.
@property {numeric} params.val_b Parameter value for b input.
@property {numeric} params.min Numeric range min.
@property {numeric} params.max Numeric range max.

@returns {HTMLElement} Element group containing range and numeric input elements.
*/
export default function slider_ab(params) {

  params.group_id ??= params.data_id || 'slider_ab'

  params.step ??= 1

  const minInputParams = {
    ...params,
    data_id: 'a',
    value: params.val_a,
    rangeInput: 'minRangeInput',
    dynamicWidth: true,
    callback: params.callback_a,
    numericChecks
  }

  // Create numericInput element for formatting and numeric checks.
  const minNumericInput = mapp.ui.elements.numericInput(minInputParams)

  const maxInputParams = {
    ...params,
    data_id: 'b',
    value: params.val_b,
    rangeInput: 'maxRangeInput',
    dynamicWidth: true,
    callback: params.callback_b,
    numericChecks
  }

  // Create numericInput element for formatting and numeric checks.
  const maxNumericInput = mapp.ui.elements.numericInput(maxInputParams)

  /**
  @function numericChecks

  @description
  The numericChecks method checks whether a provided numeric value is a number, larger than params.min, and smaller than params.max.

  The slider_ab numericCheck methods returns false if the 'a' slider element value exceeds the 'b' slider element value or vice versa.

  @param {Object} value The numeric value to check.
  @param {Object} params The config object argument.
  @property {numeric} params.min Value must be larger than min.
  @property {numeric} params.max Value must be smaller than max.
  @property {string} params.data_id The id of the numeric input element.

  @returns {Boolean} true if checks are passed.
  */
  function numericChecks(value, params) {

    // Check whether value is a null.
    if (value === null) return false;
    // Check whether value is a number.
    if (isNaN(value)) return false;

    //Value should be cast to number
    value = Number(value);

    if (params.data_id === 'a' && value > Number(maxInputParams.newValue ?? maxInputParams.value)) {

      return false
    }

    if (params.data_id === 'b' && value < Number(minInputParams.newValue ?? minInputParams.value)) {

      return false
    }

    // Check if the value is within the allowed range
    return !(value < params.min || value > params.max);

  }

  const element = mapp.utils.html.node`
    <div
      role="group"
      data-id=${params.group_id}
      class="input-range multi"
      style=${`
        --min: ${params.min};
        --max: ${params.max};
        --a: ${params.val_a};
        --b: ${params.val_b};`}>
      <div 
        class="label-row">
        ${minNumericInput} ${maxNumericInput}
      </div>
      <div class="track-bg"></div>
      <input data-id="a" type="range"
        name="minRangeInput"
        min=${params.min}
        max=${params.max}
        step=${params.step}
        value=${params.val_a}
        oninput=${e => onRangeInput(e, params)}/>
      <input data-id="b" type="range"
        name="maxRangeInput"
        min=${params.min}
        max=${params.max}
        step=${params.step}
        value=${params.val_b}
        oninput=${e => onRangeInput(e, params)}/>`

  // The sliderElement property is required to update the range input on numeric input.
  minInputParams.sliderElement = element
  maxInputParams.sliderElement = element

  /**
  @function onRangeInput

  @description
  Assign value from range type input to associated numericInput element.

  Formatting and numeric checks will be handled by the numericInput element.

  @param {Object} e oninput event from range type input.
  @param {Object} params params object used to pass additional params to the numericInput input function.
  */
  function onRangeInput(e, params) {

    // Range type input return a string target.value.
    const val = Number(e.target.value)

    // Check whether input event is from minRangeInput.
    if (e.target.dataset.id === 'a') {

      //Needed to indicate that the change is coming from a slider element
      minInputParams.onRangeInput = true;
      minNumericInput.value = val

      // Trigger formatting and numeric checks.
      minNumericInput.dispatchEvent(new Event('change'))
    }

    // Check whether input event is from maxRangeInput.
    if (e.target.dataset.id === 'b') {

      //Needed to indicate that the change is coming from a slider element
      maxInputParams.onRangeInput = true;
      maxNumericInput.value = val

      // Trigger formatting and numeric checks.
      maxNumericInput.dispatchEvent(new Event('change'))
    }
  }

  return element
}