leongersen / noUiSlider

noUiSlider is a lightweight, ARIA-accessible JavaScript range slider with multi-touch and keyboard support. It is fully GPU animated: no reflows, so it is fast; even on older devices. It also fits wonderfully in responsive designs and has no dependencies.
https://refreshless.com/nouislider/
MIT License
5.69k stars 656 forks source link

Feature: update for merge tooltip function #1244

Open Always-prog opened 1 year ago

Always-prog commented 1 year ago

Hello! Thank you for a good slider!

I needed to merge sliders if they overlapped and found a function in the documentation (https://refreshless.com/nouislider/examples/#section-merging-tooltips) that could help me achieve this. However, I had to set a hardcoded minimum proximity, which was not ideal since my tooltips have different sizes.

To address this issue, I updated the script for merging tooltips. Here's the code I used:

/**
 * @param slider HtmlElement with an initialized slider
 * @param separator String joining tooltips
 */
export function mergeTooltips(slider, separator) {
  var textIsRtl = window.getComputedStyle(slider).direction === "rtl";
  var isRtl = slider.noUiSlider.options.direction === "rtl";
  var isVertical = slider.noUiSlider.options.orientation === "vertical";
  var tooltips = slider.noUiSlider.getTooltips();
  var origins = slider.noUiSlider.getOrigins();

  // Move tooltips into the origin element. The default stylesheet handles this.
  tooltips.forEach(function (tooltip, index) {
    if (tooltip) {
      origins[index].appendChild(tooltip);
    }
  });

  slider.noUiSlider.on("update", function (
    values,
    handle,
    unencoded,
    tap,
    positions
  ) {
    var pools = [[]];
    var poolPositions = [[]];
    var poolValues = [[]];
    var atPool = 0;

    // Assign the first tooltip to the first pool, if the tooltip is configured
    if (tooltips[0]) {
      pools[0][0] = 0;
      poolPositions[0][0] = positions[0];
      poolValues[0][0] = values[0];
    }

    for (var i = 1; i < positions.length; i++) {
     // HERE's MY CODE
      const tooltipLeft = tooltips[i - 1].getBoundingClientRect();
      const tooltipRight = tooltips[i].getBoundingClientRect();
      if (
        !(
          tooltipLeft.x < tooltipRight.x &&
          tooltipLeft.x + tooltipLeft.width > tooltipRight.x
        )
      ) {
        atPool++;
        pools[atPool] = [];
        poolValues[atPool] = [];
        poolPositions[atPool] = [];
      }
      // HERE's MY CODE END

      if (tooltips[i]) {
        pools[atPool].push(i);
        poolValues[atPool].push(values[i]);
        poolPositions[atPool].push(positions[i]);
      }
    }

    pools.forEach(function (pool, poolIndex) {
      var handlesInPool = pool.length;

      for (var j = 0; j < handlesInPool; j++) {
        var handleNumber = pool[j];

        if (j === handlesInPool - 1) {
          var offset = 0;

          poolPositions[poolIndex].forEach(function (value) {
            offset += 1000 - value;
          });

          var direction = isVertical ? "bottom" : "right";
          var last = isRtl ? 0 : handlesInPool - 1;
          var lastOffset = 1000 - poolPositions[poolIndex][last];
          offset =
            (textIsRtl && !isVertical ? 100 : 0) +
            offset / handlesInPool -
            lastOffset;

          // Center this tooltip over the affected handles
          tooltips[handleNumber].innerHTML = poolValues[poolIndex].join(
            separator
          );
          tooltips[handleNumber].style.removeProperty("visibility");

          tooltips[handleNumber].style[direction] = offset + "%";
        } else {
          // Hide this tooltip
          tooltips[handleNumber].style.visibility = "hidden";
        }
      }
    });
  });
}

My approach uses tooltip position and widths to determine which tooltips need to be merged, which I believe is an improvement over a hardcoded percentage.

Thank you for taking the time to read my issue and for any feedback you may have.

leongersen commented 1 year ago

Thanks for your feedback! If you want to generalize the example in the documentation, I'd be open for a PR :)

Always-prog commented 1 year ago

@leongersen Of course, here is it! https://github.com/leongersen/noUiSlider/pull/1256

Always-prog commented 1 year ago

@leongersen Hello! Just remind you about the PR!

Always-prog commented 1 year ago

@leongersen Hi! Please take a look at the changed docs in this PR!