From e19f375c84fb02ffe1c387af2a193d21d7d40919 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Tue, 23 Jun 2020 17:24:01 +0200 Subject: [PATCH] Update slider to handle merging of overlapping tooltips (#8074) --- src/Umbraco.Web.UI.Client/package.json | 2 +- .../components/umbrangeslider.directive.js | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 167bf53eb8..816d4e5090 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -38,7 +38,7 @@ "lazyload-js": "1.0.0", "moment": "2.22.2", "ng-file-upload": "12.2.13", - "nouislider": "14.1.1", + "nouislider": "14.4.0", "npm": "6.13.6", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js index 21a1f181a6..e467522c84 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js @@ -118,6 +118,8 @@ For extra details about options and events take a look here: https://refreshless // create new slider noUiSlider.create(sliderInstance, options); + mergeTooltips(sliderInstance, 15, ' - '); + if (ctrl.onSetup) { ctrl.onSetup({ slider: sliderInstance @@ -200,6 +202,91 @@ For extra details about options and events take a look here: https://refreshless } } + // Merging overlapping tooltips: https://refreshless.com/nouislider/examples/#section-merging-tooltips + + /** + * @param slider HtmlElement with an initialized slider + * @param threshold Minimum proximity (in percentages) to merge tooltips + * @param separator String joining tooltips + */ + function mergeTooltips(slider, threshold, separator) { + + var textIsRtl = 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++) { + if (!tooltips[i] || (positions[i] - positions[i - 1]) > threshold) { + atPool++; + pools[atPool] = []; + poolValues[atPool] = []; + poolPositions[atPool] = []; + } + + 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 - 10 * value; + }); + + var direction = isVertical ? 'bottom' : 'right'; + var last = isRtl ? 0 : handlesInPool - 1; + var lastOffset = 1000 - 10 * poolPositions[poolIndex][last]; + offset = (textIsRtl && !isVertical ? 100 : 0) + (offset / handlesInPool) - lastOffset; + + // Filter to unique values + var tooltipValues = poolValues[poolIndex].filter((v, i, a) => a.indexOf(v) === i); + + // Center this tooltip over the affected handles + tooltips[handleNumber].innerHTML = tooltipValues.join(separator); + tooltips[handleNumber].style.display = 'block'; + tooltips[handleNumber].style[direction] = offset + '%'; + } else { + // Hide this tooltip + tooltips[handleNumber].style.display = 'none'; + } + } + }); + }); + } + } angular.module('umbraco.directives').component('umbRangeSlider', umbRangeSlider);