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.64k stars 658 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 11 months 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 11 months ago

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

Always-prog commented 11 months ago

@leongersen Hello! Just remind you about the PR!

Always-prog commented 10 months ago

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