// https://github.com/johnwalley/d3-simple-slider Version 1.4.1. Copyright 2018 John Walley. (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array'), require('d3-axis'), require('d3-dispatch'), require('d3-drag'), require('d3-ease'), require('d3-scale'), require('d3-selection'), require('d3-transition')) : typeof define === 'function' && define.amd ? define(['exports', 'd3-array', 'd3-axis', 'd3-dispatch', 'd3-drag', 'd3-ease', 'd3-scale', 'd3-selection', 'd3-transition'], factory) : (global = global || self, factory(global.d3 = global.d3 || {}, global.d3, global.d3, global.d3, global.d3, global.d3, global.d3, global.d3)); }(this, function (exports, d3Array, d3Axis, d3Dispatch, d3Drag, d3Ease, d3Scale, d3Selection) { 'use strict'; var UPDATE_DURATION = 200; var SLIDER_END_PADDING = 8; var top = 1; var right = 2; var bottom = 3; var left = 4; function translateX(x) { return 'translate(' + x + ',0)'; } function translateY(y) { return 'translate(0,' + y + ')'; } function slider(orientation, scale) { scale = typeof scale !== 'undefined' ? scale : null; var value = [0]; var defaultValue = [0]; var domain = [0, 10]; var width = 100; var height = 100; var displayValue = true; var handle = 'M-5.5,-5.5v10l6,5.5l6,-5.5v-10z'; var step = null; var tickValues = null; var marks = null; var tickFormat = null; var ticks = null; var displayFormat = null; var fill = null; var listeners = d3Dispatch.dispatch('onchange', 'start', 'end', 'drag'); var selection = null; var identityClamped = null; var handleIndex = null; var k = orientation === top || orientation === left ? -1 : 1; var x = orientation === left || orientation === right ? 'y' : 'x'; var y = orientation === left || orientation === right ? 'x' : 'y'; var transformAlong = orientation === top || orientation === bottom ? translateX : translateY; var transformAcross = orientation === top || orientation === bottom ? translateY : translateX; var axisFunction = null; switch (orientation) { case top: axisFunction = d3Axis.axisTop; break; case right: axisFunction = d3Axis.axisRight; break; case bottom: axisFunction = d3Axis.axisBottom; break; case left: axisFunction = d3Axis.axisLeft; break; } var handleSelection = null; var fillSelection = null; var textSelection = null; if (scale) { domain = [d3Array.min(scale.domain()), d3Array.max(scale.domain())]; if (orientation === top || orientation === bottom) { width = d3Array.max(scale.range()) - d3Array.min(scale.range()); } else { height = d3Array.max(scale.range()) - d3Array.min(scale.range()); } scale = scale.clamp(true); } function slider(context) { selection = context.selection ? context.selection() : context; if (scale) { scale = scale.range([ d3Array.min(scale.range()), d3Array.min(scale.range()) + (orientation === top || orientation === bottom ? width : height), ]); } else { scale = domain[0] instanceof Date ? d3Scale.scaleTime() : d3Scale.scaleLinear(); scale = scale .domain(domain) .range([ 0, orientation === top || orientation === bottom ? width : height, ]) .clamp(true); } identityClamped = d3Scale.scaleLinear() .range(scale.range()) .domain(scale.range()) .clamp(true); // Ensure value is valid value = value.map(function(d) { return d3Scale.scaleLinear() .range(domain) .domain(domain) .clamp(true)(d); }); tickFormat = tickFormat || scale.tickFormat(); displayFormat = displayFormat || tickFormat || scale.tickFormat(); var axis = selection.selectAll('.axis').data([null]); axis .enter() .append('g') .attr('transform', transformAcross(k * 7)) .attr('class', 'axis'); var slider = selection.selectAll('.slider').data([null]); var sliderEnter = slider .enter() .append('g') .attr('class', 'slider') .attr( 'cursor', orientation === top || orientation === bottom ? 'ew-resize' : 'ns-resize' ) .call( d3Drag.drag() .on('start', dragstarted) .on('drag', dragged) .on('end', dragended) ); // sliderEnter // .append('line') // .attr('class', 'track') // .attr(x + '1', scale.range()[0] - SLIDER_END_PADDING) // .attr('stroke', '#bbb') // .attr('stroke-width', 12) // .attr('stroke-linecap', 'round'); sliderEnter .append('line') .attr('class', 'track-inset') .attr(x + '1', scale.range()[0] - SLIDER_END_PADDING) .attr('stroke', '#eee') .attr('stroke-width', 10) .attr('stroke-linecap', 'round'); if (fill) { sliderEnter .append('line') .attr('class', 'track-fill') .attr( x + '1', value.length === 1 ? scale.range()[0] - SLIDER_END_PADDING : scale(value[0]) ) .attr('stroke', fill) .attr('stroke-width', 10) .attr('stroke-linecap', 'round'); } sliderEnter .append('line') .attr('class', 'track-overlay') .attr(x + '1', scale.range()[0] - SLIDER_END_PADDING) .attr('stroke', 'transparent') .attr('stroke-width', 40) .attr('stroke-linecap', 'round') .merge(slider.select('.track-overlay')); handleSelection = sliderEnter.selectAll('.parameter-value').data(value); var handleEnter = handleSelection .enter() .append('g') .attr('class', 'parameter-value') .attr('transform', function(d) { return transformAlong(scale(d)); }) .attr('font-family', 'sans-serif') .attr( 'text-anchor', orientation === right ? 'start' : orientation === left ? 'end' : 'middle' ); handleEnter .append('path') .attr('transform', 'rotate(' + (orientation + 1) * 90 + ')') .attr('d', handle) .attr('focusable','true') .attr('tabindex',0) .attr('id','handle') .attr('aria-label','handle') .attr('fill', 'white') .attr('stroke', '#777'); handleEnter .append('path') .attr('transform', 'translate(0 24)') .attr('d', d3.symbol() .type(d3.symbolTriangle) .size(150)) .style('fill', '#206095') .style('stroke',"none") if (displayValue && value.length === 1) { handleEnter .append('text') .attr('font-size', 10) // TODO: Remove coupling to font-size in d3-axis .attr(y, k * 27) .attr( 'dy', orientation === top ? '0em' : orientation === bottom ? '.71em' : '.32em' ) .text(tickFormat(value[0])); var text=handleEnter.select('text') var bbox = text.node().getBBox(); handleEnter.select('text').remove() var padding = 5; var rect = handleEnter.append("rect") .attr("x", bbox.x - padding) .attr("y", bbox.y - padding) .attr("ry",5) .attr("width", bbox.width + (padding*2)) .attr("height", bbox.height + (padding*2)) .style("fill", "#206095"); handleEnter .append('text') .text(tickFormat(value[0])) .style('fill','white') .attr('font-weight',700) .attr('y',text.attr('y')) .attr('dy',text.attr('dy')) } context .select('.track') .attr(x + '2', scale.range()[1] + SLIDER_END_PADDING); context .select('.track-inset') .attr(x + '2', scale.range()[1] + SLIDER_END_PADDING); if (fill) { context .select('.track-fill') .attr(x + '2', value.length === 1 ? scale(value[0]) : scale(value[1])); } context .select('.track-overlay') .attr(x + '2', scale.range()[1] + SLIDER_END_PADDING); context.select('.axis').call( axisFunction(scale) .tickFormat(tickFormat) .ticks(ticks) .tickValues(tickValues) ); // https://bl.ocks.org/mbostock/4323929 selection .select('.axis') .select('.domain') .remove(); context.select('.axis').attr('transform', transformAcross(k * 7)); context .selectAll('.axis text') .attr('fill', '#aaa') .attr(y, k * 20) .attr( 'dy', orientation === top ? '0em' : orientation === bottom ? '.71em' : '.32em' ) .attr( 'text-anchor', orientation === right ? 'start' : orientation === left ? 'end' : 'middle' ); context.selectAll('.axis line').attr('stroke', '#aaa'); context.selectAll('.parameter-value').attr('transform', function(d) { return transformAlong(scale(d)); }); fadeTickText(); function dragstarted() { d3Selection.select(this).classed('active', true); var pos = identityClamped( orientation === bottom || orientation === top ? d3Selection.event.x : d3Selection.event.y ); handleIndex = d3Array.scan( value.map(function(d) { return Math.abs(d - alignedValue(scale.invert(pos))); }) ); var newValue = value.map(function(d, i) { return i === handleIndex ? alignedValue(scale.invert(pos)) : d; }); updateHandle(newValue); listeners.call( 'start', slider, newValue.length === 1 ? newValue[0] : newValue ); updateValue(newValue, true); } function dragged() { var pos = identityClamped( orientation === bottom || orientation === top ? d3Selection.event.x : d3Selection.event.y ); var adjustedValue = alignedValue(scale.invert(pos)); var newValue = value.map(function(d, i) { if (value.length === 2) { return i === handleIndex ? handleIndex === 0 ? Math.min(adjustedValue, alignedValue(value[1])) : Math.max(adjustedValue, alignedValue(value[0])) : d; } else { return i === handleIndex ? adjustedValue : d; } }); updateHandle(newValue); listeners.call( 'drag', slider, newValue.length === 1 ? newValue[0] : newValue ); updateValue(newValue, true); } function dragended() { d3Selection.select(this).classed('active', false); var pos = identityClamped( orientation === bottom || orientation === top ? d3Selection.event.x : d3Selection.event.y ); var newValue = value.map(function(d, i) { return i === handleIndex ? alignedValue(scale.invert(pos)) : d; }); updateHandle(newValue); listeners.call( 'end', slider, newValue.length === 1 ? newValue[0] : newValue ); updateValue(newValue, true); handleIndex = null; } textSelection = selection.select('.parameter-value text'); fillSelection = selection.select('.track-fill'); } function fadeTickText() { if (displayValue && value.length === 1) { var distances = []; selection.selectAll('.axis .tick').each(function(d) { distances.push(Math.abs(d - value[0])); }); var index = d3Array.scan(distances); selection.selectAll('.axis .tick text').attr('opacity', function(d, i) { return i === index ? 0 : 1; }); } } function alignedValue(newValue) { if (step) { var valueModStep = (newValue - domain[0]) % step; var alignValue = newValue - valueModStep; if (valueModStep * 2 > step) { alignValue += step; } return newValue instanceof Date ? new Date(alignValue) : alignValue; } if (marks) { var index = d3Array.scan( marks.map(function(d) { return Math.abs(newValue - d); }) ); return marks[index]; } return newValue; } function updateValue(newValue, notifyListener) { if (value !== newValue) { value = newValue; if (notifyListener) { listeners.call( 'onchange', slider, newValue.length === 1 ? newValue[0] : newValue ); } fadeTickText(); } } function updateHandle(newValue, animate) { animate = typeof animate !== 'undefined' ? animate : false; if (animate) { selection .selectAll('.parameter-value') .data(newValue) .transition() .ease(d3Ease.easeQuadOut) .duration(UPDATE_DURATION) .attr('transform', function(d) { return transformAlong(scale(d)); }); if (fill) { fillSelection .transition() .ease(d3Ease.easeQuadOut) .duration(UPDATE_DURATION) .attr( x + '1', value.length === 1 ? scale.range()[0] - SLIDER_END_PADDING : scale(newValue[0]) ) .attr( x + '2', value.length === 1 ? scale(newValue[0]) : scale(newValue[1]) ); } } else { selection .selectAll('.parameter-value') .data(newValue) .attr('transform', function(d) { return transformAlong(scale(d)); }); if (fill) { fillSelection .attr( x + '1', value.length === 1 ? scale.range()[0] - SLIDER_END_PADDING : scale(newValue[0]) ) .attr( x + '2', value.length === 1 ? scale(newValue[0]) : scale(newValue[1]) ); } } if (displayValue) { textSelection.text(displayFormat(newValue[0])); } } slider.min = function(_) { if (!arguments.length) return domain[0]; domain[0] = _; return slider; }; slider.max = function(_) { if (!arguments.length) return domain[1]; domain[1] = _; return slider; }; slider.domain = function(_) { if (!arguments.length) return domain; domain = _; return slider; }; slider.width = function(_) { if (!arguments.length) return width; width = _; return slider; }; slider.height = function(_) { if (!arguments.length) return height; height = _; return slider; }; slider.tickFormat = function(_) { if (!arguments.length) return tickFormat; tickFormat = _; return slider; }; slider.displayFormat = function(_) { if (!arguments.length) return displayFormat; displayFormat = _; return slider; }; slider.ticks = function(_) { if (!arguments.length) return ticks; ticks = _; return slider; }; slider.value = function(_) { if (!arguments.length) { if (value.length === 1) { return value[0]; } return value; } var toArray = Array.isArray(_) ? _ : [_]; var pos = toArray.map(scale).map(identityClamped); var newValue = pos.map(scale.invert).map(alignedValue); updateHandle(newValue, true); updateValue(newValue, true); return slider; }; slider.silentValue = function(_) { if (!arguments.length) { if (value.length === 1) { return value[0]; } return value; } var toArray = Array.isArray(_) ? _ : [_]; var pos = toArray.map(scale).map(identityClamped); var newValue = pos.map(scale.invert).map(alignedValue); updateHandle(newValue, false); updateValue(newValue, false); return slider; }; slider.default = function(_) { if (!arguments.length) { if (defaultValue.length === 1) { return defaultValue[0]; } return defaultValue; } var toArray = Array.isArray(_) ? _ : [_]; defaultValue = toArray; value = toArray; return slider; }; slider.step = function(_) { if (!arguments.length) return step; step = _; return slider; }; slider.tickValues = function(_) { if (!arguments.length) return tickValues; tickValues = _; return slider; }; slider.marks = function(_) { if (!arguments.length) return marks; marks = _; return slider; }; slider.handle = function(_) { if (!arguments.length) return handle; handle = _; return slider; }; slider.displayValue = function(_) { if (!arguments.length) return displayValue; displayValue = _; return slider; }; slider.fill = function(_) { if (!arguments.length) return fill; fill = _; return slider; }; slider.on = function() { var value = listeners.on.apply(listeners, arguments); return value === listeners ? slider : value; }; return slider; } function sliderHorizontal(scale) { return slider(bottom, scale); } function sliderVertical(scale) { return slider(left, scale); } function sliderTop(scale) { return slider(top, scale); } function sliderRight(scale) { return slider(right, scale); } function sliderBottom(scale) { return slider(bottom, scale); } function sliderLeft(scale) { return slider(left, scale); } exports.sliderHorizontal = sliderHorizontal; exports.sliderVertical = sliderVertical; exports.sliderTop = sliderTop; exports.sliderRight = sliderRight; exports.sliderBottom = sliderBottom; exports.sliderLeft = sliderLeft; Object.defineProperty(exports, '__esModule', { value: true }); }));