@flourish/controls
Advanced tools
Comparing version
{ | ||
"name": "@flourish/controls", | ||
"version": "0.4.2", | ||
"version": "1.0.0-alpha1", | ||
"description": "Switchable dropdown/buttons/slider control", | ||
@@ -23,5 +23,6 @@ "main": "controls.js", | ||
"dependencies": { | ||
"@flourish/slider": "^1.3.1", | ||
"@flourish/slider": "^1.4.0", | ||
"d3-selection": "^1.1.0", | ||
"rollup": "^0.65.0", | ||
"d3-time-format": "^2.1.3", | ||
"rollup": "^0.67.4", | ||
"rollup-plugin-node-resolve": "^3.0.0", | ||
@@ -32,3 +33,3 @@ "uglify-js": "^3.4.9" | ||
"@flourish/eslint-plugin-flourish": "^0.7.2", | ||
"eslint": "^5.5.0", | ||
"eslint": "^5.9.0", | ||
"pre-commit": "^1.2.2" | ||
@@ -35,0 +36,0 @@ }, |
import { select } from "d3-selection"; | ||
function createButtons(core, dropdown_obj) { | ||
var container = core.container; | ||
var props = core.props; | ||
var onChangeCallbacks = core.onChangeCallbacks; | ||
var button_container = container.append("div").attr("class", "button-container"); | ||
var buttons, button_nodes; | ||
var setStyles = function() { | ||
}; | ||
function createButtons(control_obj, state, container) { | ||
var button_obj = {}; | ||
var button_container = select(container).append("div").attr("class", "button-container"); | ||
var show = function() { | ||
setStyles(); | ||
if (props.buttons_type === "floating") { | ||
button_container.style("width", null) | ||
.style("display", "inline-block") | ||
.classed("button-group", false); | ||
} | ||
else { | ||
button_container.style("width", props.width) | ||
.style("display", "table") | ||
.classed("button-group", true); | ||
if (props.buttons_type === "auto") { | ||
var buttons_fit = button_nodes.every(function(el) { | ||
return el.offsetWidth >= el.scrollWidth; // https://stackoverflow.com/a/36723850 | ||
}); | ||
if (!buttons_fit) { | ||
hide(); | ||
dropdown_obj.show(); | ||
} | ||
} | ||
} | ||
var showControl = function() { | ||
var grouped = state.control !== "floating-buttons"; | ||
button_container.style("width", grouped ? state.width + "px" : null) | ||
.style("display", grouped ? "table" : "inline-block") | ||
.classed("button-group", grouped); | ||
}; | ||
var hide = function() { | ||
var hideControl = function() { | ||
button_container.style("display", "none"); | ||
}; | ||
var setValue = function() { | ||
buttons && buttons.each(function(d, i) { select(this).classed("selected", i===props.index); }); | ||
}; | ||
var setOptions = function() { | ||
buttons = button_container.text("").selectAll(".button") | ||
.data(props.options) | ||
button_obj.show = showControl; | ||
button_obj.hide = hideControl; | ||
button_obj.update = function(sorted_options) { | ||
button_container.text(""); | ||
if (!control_obj.n_options || ["grouped-buttons", "floating-buttons", "auto"].indexOf(state.control) === -1) { | ||
hideControl(); | ||
return button_obj; | ||
} | ||
var index = control_obj.index(); | ||
var buttons = button_container.selectAll(".button") | ||
.data(sorted_options) | ||
.enter() | ||
.append("div") | ||
.attr("class", "button") | ||
.classed("selected", function(d, i) { return i === props.index; }) | ||
.text(function(d) { return d; }) | ||
.attr("title", function(d) { return d; }) | ||
.on("click", function(d, i) { | ||
if (i === props.index) return; | ||
props.index = i; | ||
onChangeCallbacks(); | ||
.append("div"); | ||
buttons.attr("class", "button") | ||
.classed("selected", function(d) { return d.options_index === index; }) | ||
.text(function(d) { return d.display; }) | ||
.attr("title", function(d) { return d.display; }) | ||
.on("click", function(d) { | ||
var i = d.options_index; | ||
if (i === control_obj.index()) return; | ||
control_obj.index(i); | ||
buttons.classed("selected", function(d) { return d.options_index === i; }); | ||
control_obj.trigger("change"); | ||
}); | ||
button_nodes = buttons.nodes(); | ||
showControl(); | ||
}; | ||
var remove = function() { | ||
button_container.remove(); | ||
button_obj.checkFit = function() { | ||
if (!button_container.classed("button-group")) return; | ||
showControl(); | ||
var buttons_fit = button_container.selectAll(".button").nodes().every(function(el) { | ||
return el.offsetWidth >= el.scrollWidth; // https://stackoverflow.com/a/36723850 | ||
}); | ||
if (!buttons_fit) hideControl(); | ||
return buttons_fit; | ||
}; | ||
var buttons_obj = { handle: button_container }; | ||
buttons_obj.show = function() { show(); return buttons_obj; }; | ||
buttons_obj.hide = function() { hide(); return buttons_obj; }; | ||
buttons_obj.setValue = function() { setValue(); return buttons_obj; }; | ||
buttons_obj.setOptions = function() { setOptions(); return buttons_obj; }; | ||
buttons_obj.remove = function() { remove(); return buttons_obj; }; | ||
return buttons_obj; | ||
return button_obj; | ||
} | ||
@@ -74,0 +67,0 @@ |
import { select } from "d3-selection"; | ||
import { makeBackgroundString } from "../core/css.js"; | ||
import { makeBackgroundString } from "../lib/css.js"; | ||
function createDropdownIcon(colour) { | ||
function createDropdownIcon(color) { | ||
var start_string = '<svg width="6px" height="12px" viewBox="0 0 6 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <g transform="translate(-442.000000, -44.000000)" fill="'; | ||
var end_string = '"> <g id="Group" transform="translate(442.000000, 44.000000)"> <polygon id="Triangle-2" points="3 0 6 5 0 5"></polygon> <polygon id="Triangle-2-Copy" points="3 12 0 7 6 7"></polygon> </g> </g> </svg>'; | ||
return makeBackgroundString(start_string + colour + end_string); | ||
return makeBackgroundString(start_string + color + end_string); | ||
} | ||
function createDropdown(core) { | ||
var container = core.container; | ||
var props = core.props; | ||
var onChangeCallbacks = core.onChangeCallbacks; | ||
function createDropdown(control_obj, state, container) { | ||
var dropdown_obj = {}; | ||
// Add dropdown elements to DOM | ||
var dropdown = container.append("div").attr("class", "dropdown"); | ||
// Add dropdown elements to container | ||
var dropdown = select(container).append("div").attr("class", "dropdown"); | ||
var dropdown_node = dropdown.node(); | ||
var dropdown_main = dropdown.append("div").attr("class", "main"); | ||
@@ -24,2 +23,12 @@ var dropdown_current = dropdown_main.append("span").attr("class", "current"); | ||
var showDropdownList = function() { | ||
dropdown.classed("open", true); | ||
dropdown_list.style("top", "100%"); | ||
dropdown_list.style("bottom", null); | ||
if (dropdown_list.node().getBoundingClientRect().bottom > window.innerHeight) { | ||
dropdown_list.style("top", null); | ||
dropdown_list.style("bottom", "100%"); | ||
} | ||
}; | ||
var hideDropdownList = function() { | ||
@@ -30,7 +39,20 @@ dropdown.classed("open", false); | ||
var toggleDropdownList = function() { | ||
dropdown.classed("open", !dropdown.classed("open")); | ||
if (dropdown.classed("open")) hideDropdownList(); | ||
else showDropdownList(); | ||
}; | ||
var dropdown_node = dropdown.node(); | ||
document.querySelector("body").addEventListener("click", function(event) { | ||
var setStyles = (function() { | ||
var icon_color; | ||
return function() { | ||
if (icon_color !== state.dropdown_icon_color) { | ||
icon_color = state.dropdown_icon_color; | ||
dropdown_symbol.style("background-image", createDropdownIcon(icon_color)); | ||
} | ||
}; | ||
})(); | ||
dropdown_main.on("click", function() { toggleDropdownList(); }); | ||
var clickHandler = function() { | ||
if (!dropdown.classed("open")) return; // If already closed, nothing to close | ||
@@ -45,24 +67,10 @@ var el = event.target; | ||
hideDropdownList(); // Clicked somewhere else, hide the dropdown | ||
}, false); | ||
}; | ||
dropdown_main.on("click", function() { toggleDropdownList(); }); | ||
var setStyles = (function() { | ||
var icon_colour; | ||
return function() { | ||
if (icon_colour !== props.dropdown_icon_colour) { | ||
icon_colour = props.dropdown_icon_colour; | ||
dropdown_symbol.style("background-image", createDropdownIcon(icon_colour)); | ||
} | ||
}; | ||
})(); | ||
var show = function() { | ||
setStyles(); | ||
dropdown.style("width", props.width) | ||
.style("display", "inline-table"); | ||
dropdown.select(".main").style("width", props.width); | ||
var showControl = function() { | ||
dropdown.style("width", state.width + "px").style("display", "inline-table"); | ||
dropdown.select(".main").style("width", state.width + "px"); | ||
}; | ||
var hide = function() { | ||
var hideControl = function() { | ||
hideDropdownList(); | ||
@@ -72,32 +80,51 @@ dropdown.style("display", "none"); | ||
var setValue = function() { | ||
dropdown_current.text(props.value).attr("title", props.value); | ||
dropdown_obj.appendedToDOM = function() { | ||
document.querySelector("body").addEventListener("click", clickHandler, false); | ||
return dropdown_obj; | ||
}; | ||
var setOptions = function() { | ||
dropdown_obj.removedFromDOM = function() { | ||
document.querySelector("body").removeEventListener("click", clickHandler); | ||
return dropdown_obj; | ||
}; | ||
dropdown_obj.show = showControl; | ||
dropdown_obj.hide = hideControl; | ||
dropdown_obj.update = function(sorted_options) { | ||
dropdown_list.text(""); | ||
if (!control_obj.n_options || (state.control !== "dropdown" && state.control !== "auto")) { | ||
hideControl(); | ||
return dropdown_obj; | ||
} | ||
dropdown_list.text("") | ||
.selectAll(".list-item") | ||
.data(props.options) | ||
.data(sorted_options) | ||
.enter() | ||
.append("div") | ||
.attr("class", "list-item") | ||
.text(function(d) { return d; }) | ||
.on("click", function(d, i) { | ||
.text(function(d) { return d.display; }) | ||
.on("click", function(d) { | ||
hideDropdownList(); | ||
if (select(this).classed("selected")) return; | ||
props.index = i; | ||
onChangeCallbacks(); | ||
var i = d.options_index; | ||
if (i === control_obj.index()) return; | ||
control_obj.index(i); | ||
dropdown_current.text(d.display).attr("title", d.display); | ||
control_obj.trigger("change"); | ||
}); | ||
}; | ||
var remove = function() { | ||
dropdown.remove(); | ||
var sorted_index = control_obj.getSortedIndex(); | ||
var value = sorted_options[sorted_index].display; | ||
dropdown_current.text(value).attr("title", value); | ||
setStyles(); | ||
showControl(); | ||
return dropdown_obj; | ||
}; | ||
var dropdown_obj = { handle: dropdown }; | ||
dropdown_obj.show = function() { show(); return dropdown_obj; }; | ||
dropdown_obj.hide = function() { hide(); return dropdown_obj; }; | ||
dropdown_obj.setValue = function() { setValue(); return dropdown_obj; }; | ||
dropdown_obj.setOptions = function() { setOptions(); return dropdown_obj; }; | ||
dropdown_obj.remove = function() { remove(); return dropdown_obj; }; | ||
@@ -104,0 +131,0 @@ return dropdown_obj; |
@@ -0,5 +1,5 @@ | ||
import { select } from "d3-selection"; | ||
import Slider from "@flourish/slider"; | ||
import { makeBackgroundString } from "../core/css.js"; | ||
import { makeBackgroundString } from "../lib/css.js"; | ||
function createPlayButton(colour) { | ||
@@ -18,85 +18,84 @@ var start_string = '<svg width="25px" height="30px" viewBox="0 0 25 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <polygon id="Triangle" fill="'; | ||
function createSlider(core) { | ||
var container = core.container; | ||
var props = core.props; | ||
var onChangeCallbacks = core.onChangeCallbacks; | ||
var container_selector = core.container_selector; | ||
function createSlider(control_obj, state, container) { | ||
var slider_obj = {}; | ||
var slider_holder = select(container).append("div").attr("class", "slider-holder animatable"); | ||
var slider_play_button = slider_holder.append("div").attr("class", "slider-play"); | ||
var slider_div = slider_holder.append("div").attr("class", "slider"); | ||
var play_string, pause_string; | ||
var play_string, pause_string, handle_color; | ||
var sorted_options, timesteps, slider_label, is_playing; | ||
var timer_id = null; | ||
var slider_holder = container.append("div").attr("class", "slider-holder animatable"); | ||
var slider_play_button = slider_holder.append("div").attr("class", "slider-play"); | ||
slider_holder.append("div").attr("class", "slider"); | ||
var slider = Slider(container_selector + " .slider-holder .slider") | ||
var clearTimer = function() { | ||
clearTimeout(timer_id); | ||
timer_id = null; | ||
}; | ||
var sliderChangeFunction = function(i) { | ||
var d = sorted_options[i]; | ||
if (d.options_index === control_obj.index()) return; | ||
control_obj.index(d.options_index); | ||
control_obj.trigger("change"); | ||
}; | ||
var slider = Slider(slider_div.node()) | ||
.channelHeight(6) | ||
.snap(true) | ||
.on("change", function(index) { | ||
if (index === props.index) return; | ||
props.index = index; | ||
onChangeCallbacks(); | ||
.on("change", function(i) { | ||
var is_playing = timer_id !== null; | ||
if (is_playing) clearTimer(); | ||
sliderChangeFunction(i); | ||
if (is_playing) setNextTick(); | ||
}); | ||
var sliderplayer; | ||
var stopSliderPlayer = function() { | ||
if (!sliderplayer) return; | ||
clearInterval(sliderplayer); | ||
sliderplayer = undefined; | ||
clearTimer(); | ||
slider_holder.classed("playing", false); | ||
slider_play_button.style("background-image", play_string); | ||
control_obj._isPlaying_(false); | ||
is_playing = false; | ||
}; | ||
var setSliderPlayerMove = function(next_index) { | ||
if (next_index === 0 && !props.slider_loop) { | ||
stopSliderPlayer(); | ||
return; | ||
} | ||
var next_time_step = 1000* (Math.abs(props.slider_step_time) + (next_index ? 0 : props.slider_loop_pause)); | ||
var setNextTick = function() { | ||
var current_index = control_obj.getSortedIndex(); | ||
var current_delay = timesteps[current_index]; | ||
var final_index = control_obj.n_options - 1; | ||
var next_index = current_index < final_index ? current_index + 1 : 0; | ||
sliderplayer = setTimeout(function() { | ||
var n_options = props.n_options; | ||
var index = props.slider_step_time>=0 ? next_index : n_options-(1+next_index); | ||
slider.value(index).update(); | ||
props.index = index; | ||
onChangeCallbacks(); | ||
var ni = (next_index+1)%n_options; | ||
setSliderPlayerMove(ni); | ||
}, next_time_step); | ||
timer_id = setTimeout(function() { | ||
sliderChangeFunction(next_index); | ||
if (state.loop || next_index < final_index) setNextTick(); | ||
else stopSliderPlayer(); | ||
}, current_delay); | ||
}; | ||
var startSliderPlayer = function() { | ||
var n_options = props.n_options; | ||
if (n_options < 2) return; | ||
var final_index = n_options - 1; | ||
var index = props.index; | ||
var step_time = props.slider_step_time; | ||
slider_holder.classed("playing", true); | ||
slider_play_button.style("background-image", pause_string); | ||
if (step_time > 0 && index === final_index) { | ||
index = 0; | ||
slider.value(index).update(); | ||
props.index = index; | ||
onChangeCallbacks(); | ||
} | ||
else if (step_time < 0 && index === 0) { | ||
index = final_index; | ||
slider.value(index).update(); | ||
props.index = index; | ||
onChangeCallbacks(); | ||
} | ||
var effective_index = step_time>0 ? index : n_options-(1+index); | ||
setSliderPlayerMove(effective_index + 1); | ||
setNextTick(); | ||
control_obj._isPlaying_(true); | ||
is_playing = true; | ||
}; | ||
slider_play_button.on("click", function() { | ||
if (slider_holder.classed("playing")) stopSliderPlayer(); | ||
else if (props.slider_play_button) startSliderPlayer(); | ||
if (timer_id === null) startSliderPlayer(); | ||
else stopSliderPlayer(); | ||
}); | ||
var setWidths = function() { | ||
slider_holder.style("width", props.width); | ||
slider_holder.style("width", state.width + "px"); | ||
slider_play_button.style("display", state.play_button ? null : "none"); | ||
var holder_width = slider_holder.node().getBoundingClientRect().width; | ||
var button_width = slider_play_button.node().getBoundingClientRect().width; | ||
slider_div.style("width", Math.max((holder_width - button_width), 1) + "px"); | ||
var w = window.innerWidth; | ||
var handle_radius = w < 520 ? 12 : 15; | ||
slider.labelSize(w < 520 ? 12 : 14) | ||
@@ -111,69 +110,75 @@ .handleRadius(handle_radius) | ||
var setHandles = (function() { | ||
var handle_col; | ||
return function() { | ||
if (props.slider_play_button) { | ||
slider_holder.classed("animatable", true); | ||
slider_play_button.style("transform", props.slider_step_time < 0 ? "rotate(180deg)" : null); | ||
} | ||
else { | ||
stopSliderPlayer(); | ||
slider_holder.classed("animatable", false); | ||
} | ||
if (handle_col !== props.slider_handle_colour) { | ||
slider.update(); // Make sure slider-handle actually exists before changing its colour | ||
handle_col = props.slider_handle_colour; | ||
slider_holder.select(".slider-handle").style("fill", handle_col); | ||
play_string = createPlayButton(handle_col); | ||
pause_string = createPauseButton(handle_col); | ||
slider_play_button.style("background-image", sliderplayer ? pause_string : play_string); | ||
} | ||
}; | ||
})(); | ||
var setLabel = function() { | ||
var label; | ||
if (props.slider_label === true) label = props.value; | ||
else if (typeof props.slider_label === "string") label = props.slider_label; | ||
else label = null; | ||
slider.label(label); | ||
var setHandles = function() { | ||
if (state.play_button) { | ||
slider_holder.classed("animatable", true); | ||
} | ||
else { | ||
stopSliderPlayer(); | ||
slider_holder.classed("animatable", false); | ||
} | ||
if (handle_color !== state.slider_handle_color) { | ||
slider.update(); // Make sure slider-handle actually exists before changing its colour | ||
handle_color = state.slider_handle_color; | ||
slider_holder.select(".slider-handle").style("fill", handle_color); | ||
play_string = createPlayButton(handle_color); | ||
pause_string = createPauseButton(handle_color); | ||
slider_play_button.style("background-image", timer_id ? pause_string : play_string); | ||
} | ||
}; | ||
var setStyles = function() { | ||
var showControl = function() { | ||
slider_holder.style("display", "inline-block"); | ||
setWidths(); | ||
setHandles(); | ||
setLabel(); | ||
return slider_obj; | ||
}; | ||
var show = function() { | ||
slider_holder.style("display", "inline-block"); | ||
setStyles(); | ||
slider.update(); | ||
}; | ||
var hide = function() { | ||
var hideControl = function() { | ||
stopSliderPlayer(); | ||
slider_holder.style("display", "none"); | ||
return slider_obj; | ||
}; | ||
var setValue = function() { | ||
slider.value(props.index); | ||
}; | ||
slider_obj.show = showControl; | ||
slider_obj.hide = hideControl; | ||
var setOptions = function() { | ||
slider.domain([0, props.n_options - 1]).update(); | ||
}; | ||
var remove = function() { | ||
slider_holder.remove(); | ||
slider_obj.update = function(_sorted_options) { | ||
sorted_options = _sorted_options; | ||
if (!control_obj.n_options || state.control !== "slider") { | ||
hideControl(); | ||
return slider_obj; | ||
} | ||
showControl(); | ||
var n_options = control_obj.n_options; | ||
var loop = state.loop; | ||
timesteps = sorted_options.map(function(d, i) { | ||
var dur_in_seconds = state.step_time + (loop && i === (n_options -1) ? state.restart_pause : 0); | ||
return dur_in_seconds * 1000; | ||
}); | ||
var sorted_index = control_obj.getSortedIndex(); | ||
var d = sorted_options[sorted_index]; | ||
slider.domain([0, n_options - 1]) | ||
.value(sorted_index) | ||
.label(d.display) | ||
.channelFill(state.slider_track_color) | ||
.update(); | ||
slider_label = slider_label || slider_div.select("text.slider-label"); | ||
slider_label.style("fill", state.slider_track_color); | ||
if (control_obj._isPlaying_() && !is_playing) startSliderPlayer(); | ||
else if (!control_obj._isPlaying_() && is_playing) stopSliderPlayer(); | ||
return slider_obj; | ||
}; | ||
var slider_obj = { handle: slider_holder }; | ||
slider_obj.show = function() { show(); return slider_obj; }; | ||
slider_obj.hide = function() { hide(); return slider_obj; }; | ||
slider_obj.setValue = function() { setValue(); return slider_obj; }; | ||
slider_obj.setOptions = function() { setOptions(); return slider_obj; }; | ||
slider_obj.remove = function() { remove(); return slider_obj; }; | ||
return slider_obj; | ||
@@ -180,0 +185,0 @@ } |
216
src/index.js
@@ -1,2 +0,5 @@ | ||
import { createCore } from "./core/core.js"; | ||
import { select } from "d3-selection"; | ||
import { injectCSS } from "./lib/css.js"; | ||
import { getDefaultParser, getDefaultFormatter } from "./lib/parse-format"; | ||
import { sortArray } from "./lib/sort"; | ||
import { createDropdown } from "./controls/dropdown.js"; | ||
@@ -7,94 +10,161 @@ import { createButtons } from "./controls/buttons.js"; | ||
function createControlsGroup(container_selector) { | ||
var core = createCore(container_selector); | ||
var output_obj = core.output_obj; | ||
var props = core.props; | ||
var container = core.container; | ||
var DEFAULTS = Object.freeze({ | ||
type: "categorical", | ||
temporal_format: "%Y", | ||
sort: true, | ||
control: "auto", | ||
width: 200, | ||
play_button: true, | ||
step_time: 2, | ||
loop: true, | ||
restart_pause: 0, | ||
icon_color: "#5671AD", | ||
buttons_type: "grouped", | ||
dropdown_icon_color: "#000000", | ||
slider_handle_color: "#000000", | ||
slider_track_color: "#DDDDDD", | ||
_index_: null, | ||
_is_playing_: false | ||
}); | ||
var dropdown_obj = createDropdown(core); | ||
var buttons_obj = createButtons(core, dropdown_obj); | ||
var slider_obj = createSlider(core); | ||
var updateCurrentOptions = (function() { | ||
var options = []; | ||
return function() { | ||
var opts = props.options; | ||
var num_options = opts.length; | ||
if (num_options < 2) return; | ||
if (options && opts.length === options.length && opts.every(function(op, i) { return op === options[i]; })) { | ||
// If here we have multiple options that haven't changes since last call. | ||
return false; | ||
function init(state, getParser, getFormatter) { | ||
var control_obj = {}; | ||
getParser = getParser || getDefaultParser; | ||
getFormatter = getFormatter || getDefaultFormatter; | ||
var options = []; | ||
var sorted_options = []; | ||
var changeHandlers = []; | ||
var container = document.createElement("div"); | ||
container.setAttribute("class", "controls-container"); | ||
var dropdown_obj = createDropdown(control_obj, state, container); | ||
var buttons_obj = createButtons(control_obj, state, container); | ||
var slider_obj = createSlider(control_obj, state, container); | ||
for (var key in DEFAULTS) { | ||
if (state[key] === undefined) state[key] = DEFAULTS[key]; | ||
} | ||
var checkValidIndex = function(i) { | ||
return options.length && i >= 0 && i < options.length; | ||
}; | ||
var resizeHandler = function() { | ||
if (state.control !== "auto") return; | ||
var buttons_fit = buttons_obj.checkFit(); | ||
if (buttons_fit) dropdown_obj.hide(); | ||
else dropdown_obj.show(); | ||
}; | ||
var updateControls = function(sorted_options) { | ||
container.style.display = (sorted_options.length > 1 && state.control !== "none") ? null : "none"; | ||
container.style.width = state.width + "px"; | ||
slider_obj.update(sorted_options); // Do slider first in case we're stopping playing | ||
dropdown_obj.update(sorted_options); | ||
buttons_obj.update(sorted_options); | ||
resizeHandler(); | ||
}; | ||
control_obj.appendTo = function(parent) { | ||
injectCSS(); | ||
select(parent).node().appendChild(container); | ||
dropdown_obj.appendedToDOM(); | ||
window.addEventListener("resize", resizeHandler); | ||
resizeHandler(); | ||
return control_obj; | ||
}; | ||
var callOnChangeCallbacks = function() { | ||
var index = indexFunction(); | ||
var value = options[index]; | ||
changeHandlers.forEach(function(func) { | ||
func(value, index); | ||
}); | ||
return control_obj; | ||
}; | ||
control_obj.remove = function() { | ||
if (container.parentElement) container.parentElement.removeChild(container); | ||
dropdown_obj.removedFromDOM(); | ||
window.removeEventListener("resize", resizeHandler); | ||
return control_obj; | ||
}; | ||
control_obj.options = function(arr) { | ||
if (arr === undefined) return options.slice(); | ||
if (!Array.isArray(arr)) return control_obj; | ||
options = arr.slice(); | ||
var n = options.length; | ||
var i = indexFunction(); | ||
if (!n) indexFunction(null); | ||
else if (i === null || i >= n) indexFunction(0); | ||
return control_obj; | ||
}; | ||
Object.defineProperty(control_obj, "n_options", { get: function() { return options.length; } }); | ||
var indexFunction = (function() { | ||
var current_index = state._index_; | ||
return function(i) { | ||
if (i === undefined) return current_index; | ||
if (i === null || checkValidIndex(i)) { | ||
current_index = i; | ||
if (!state._is_playing_) state._index_ = current_index; | ||
} | ||
options = Object.freeze(opts.slice()); | ||
if (props.index > num_options - 1) props.index = num_options - 1; | ||
return true; | ||
else console.warn("Invalid index, ignoring update call"); | ||
return control_obj; | ||
}; | ||
})(); | ||
control_obj.index = indexFunction; | ||
var updateCurrentValue = (function() { | ||
var current_index, current_value; | ||
return function() { | ||
if (current_index === props.index && current_value === props.value) return false; | ||
var n_options = props.n_options; | ||
if (props.index > n_options - 1) props.index = n_options - 1; | ||
current_index = props.index; | ||
current_value = props.value; | ||
return true; | ||
}; | ||
})(); | ||
control_obj.getSortedIndex = function() { | ||
var options_index = indexFunction(); | ||
if (!state.sort) return options_index; | ||
var sorted_index; | ||
sorted_options.some(function(d, i) { | ||
if (d.options_index === options_index) { | ||
sorted_index = i; | ||
return true; | ||
} | ||
}); | ||
return sorted_index; | ||
}; | ||
var setControlOptions = function() { | ||
dropdown_obj.setOptions(); | ||
buttons_obj.setOptions(); | ||
slider_obj.setOptions(); | ||
control_obj.value = function(value) { | ||
if (value === undefined) return options[indexFunction()]; | ||
var index = options.indexOf(value); | ||
if (index !== -1) indexFunction(index); | ||
return control_obj; | ||
}; | ||
var setControlValues = function() { | ||
dropdown_obj.setValue(); | ||
buttons_obj.setValue(); | ||
slider_obj.setValue(); | ||
control_obj.on = function(event, callback) { | ||
if (event === "change") changeHandlers.push(callback.bind(control_obj)); | ||
return control_obj; | ||
}; | ||
var setVisibleControl = function(control) { | ||
if (control === "none") container.style("display", "none"); | ||
else container.style("display", null); | ||
if (control === "dropdown") dropdown_obj.show(); | ||
else dropdown_obj.hide(); | ||
if (control === "slider") slider_obj.show(); | ||
else slider_obj.hide(); | ||
if (control === "buttons") buttons_obj.show(); | ||
else buttons_obj.hide(); | ||
control_obj.update = function() { | ||
sorted_options = sortArray(options, state, getParser(), getFormatter()); | ||
updateControls(sorted_options); | ||
return control_obj; | ||
}; | ||
var update = function() { | ||
var options_updated = updateCurrentOptions(); | ||
if (props.n_options < 2) { | ||
props.index = 0; | ||
setVisibleControl("none"); | ||
return; | ||
} | ||
var value_updated = updateCurrentValue(); | ||
if (value_updated || options_updated) { | ||
if (options_updated) setControlOptions(); | ||
setControlValues(); | ||
} | ||
setVisibleControl(props.control); | ||
control_obj.trigger = function(event_type) { | ||
if (event_type === "change") callOnChangeCallbacks(); | ||
return control_obj; | ||
}; | ||
var remove = function() { | ||
dropdown_obj.remove(); | ||
buttons_obj.remove(); | ||
slider_obj.remove(); | ||
if (core.added_class) container.classed("controls-container", false); | ||
window.removeEventListener("resize", update); | ||
var isPlaying = function(is_playing) { | ||
if (is_playing === undefined) return state._is_playing_; | ||
state._is_playing_ = !!is_playing; | ||
if (!is_playing) indexFunction(indexFunction()); // Force _index_ to match current index | ||
}; | ||
window.addEventListener("resize", update); | ||
output_obj.update = function() { update(); return this; }; | ||
output_obj.remove = function() { remove(); return this; }; | ||
control_obj._isPlaying_ = isPlaying; | ||
return output_obj; | ||
return control_obj; | ||
} | ||
export default createControlsGroup; | ||
export default init; |
Sorry, the diff of this file is too big to display
162466
21.49%13
18.18%4615
19.19%6
20%+ Added
+ Added
- Removed
Updated
Updated