1 | import { select } from "d3-selection";
|
2 | import { getDefaultParser, getDefaultFormatter } from "./lib/parse-format";
|
3 | import { sortArray } from "./lib/sort";
|
4 | import { injectCSS } from "./lib/css";
|
5 | import { createDropdown } from "./controls/dropdown.js";
|
6 | import { createButtons } from "./controls/buttons.js";
|
7 | import { createSlider } from "./controls/slider.js";
|
8 | import { getRemToPx } from "./lib/remToPx";
|
9 |
|
10 |
|
11 | var DEFAULTS = Object.freeze({
|
12 | control_type: "dropdown",
|
13 |
|
14 |
|
15 | dropdown_width_mode: "auto",
|
16 | dropdown_width_fixed: 20,
|
17 |
|
18 |
|
19 | button_group: true,
|
20 | button_group_width_mode: "fixed",
|
21 | button_group_width_fixed: 20,
|
22 |
|
23 |
|
24 | slider_width: 15,
|
25 | slider_handle_color: null,
|
26 | slider_font_color: null,
|
27 | slider_background_color: "#dddddd",
|
28 |
|
29 | slider_handle_height: 1,
|
30 | slider_track_height: 0.2,
|
31 | slider_margin: 4.5,
|
32 |
|
33 | slider_play_button: true,
|
34 | slider_step_time: 2,
|
35 | slider_loop: true,
|
36 | slider_restart_pause: 0,
|
37 |
|
38 |
|
39 | sort: "unsorted",
|
40 | sort_temporal_format: "%Y",
|
41 |
|
42 | _index_: null,
|
43 | _is_playing_: false
|
44 | });
|
45 |
|
46 |
|
47 | function init(state, getParser, getFormatter) {
|
48 | var control_obj = {};
|
49 | getParser = getParser || getDefaultParser;
|
50 | getFormatter = getFormatter || getDefaultFormatter;
|
51 | var options = [];
|
52 | var sorted_options = [];
|
53 | var changeHandlers = [];
|
54 | var container = document.createElement("div");
|
55 | container.setAttribute("class", "fl-controls-container");
|
56 | var dropdown_obj = createDropdown(control_obj, state, container);
|
57 | var buttons_obj = createButtons(control_obj, state, container);
|
58 | var slider_obj = createSlider(control_obj, state, container);
|
59 |
|
60 | for (var key in DEFAULTS) {
|
61 | if (state[key] === undefined) state[key] = DEFAULTS[key];
|
62 | }
|
63 |
|
64 | var current_index = state._index_;
|
65 |
|
66 | var checkValidIndex = function(i) {
|
67 | return options.length && i >= 0 && i < options.length;
|
68 | };
|
69 |
|
70 | var updateControls = function(sorted_options) {
|
71 | container.style.display = (sorted_options.length > 1) ? null : "none";
|
72 | container.style.width = "";
|
73 | slider_obj.update(sorted_options);
|
74 | dropdown_obj.update(sorted_options);
|
75 | buttons_obj.update(sorted_options);
|
76 | };
|
77 |
|
78 | control_obj.appendTo = function(parent_container, bounding_container) {
|
79 | injectCSS();
|
80 | select(parent_container).node().appendChild(container);
|
81 | dropdown_obj.appendedToDOM(bounding_container);
|
82 | return control_obj;
|
83 | };
|
84 |
|
85 | var callOnChangeCallbacks = function() {
|
86 | var index = indexFunction();
|
87 | var value = options[index];
|
88 | changeHandlers.forEach(function(func) {
|
89 | func(value, index);
|
90 | });
|
91 | return control_obj;
|
92 | };
|
93 |
|
94 | control_obj.remove = function() {
|
95 | if (container.parentElement) container.parentElement.removeChild(container);
|
96 | dropdown_obj.removedFromDOM();
|
97 |
|
98 | return control_obj;
|
99 | };
|
100 |
|
101 | control_obj.options = function(arr) {
|
102 | if (arr === undefined) return options.slice();
|
103 | if (!Array.isArray(arr)) return control_obj;
|
104 | options = arr.slice();
|
105 | var n = options.length;
|
106 | var i = indexFunction();
|
107 | if (!n) indexFunction(null);
|
108 | else if (i === null || i >= n) indexFunction(0);
|
109 | return control_obj;
|
110 | };
|
111 |
|
112 | Object.defineProperty(control_obj, "n_options", { get: function() { return options.length; } });
|
113 |
|
114 |
|
115 | var indexFunction = function(i) {
|
116 | if (i === undefined) {
|
117 | if (!state._is_playing_) current_index = state._index_;
|
118 | return current_index;
|
119 | }
|
120 | if (i === null || checkValidIndex(i)) {
|
121 | current_index = i;
|
122 | if (!state._is_playing_) state._index_ = current_index;
|
123 | }
|
124 | else console.warn("Invalid index, ignoring update call");
|
125 | return control_obj;
|
126 | };
|
127 | control_obj.index = indexFunction;
|
128 |
|
129 | control_obj.getSortedIndex = function() {
|
130 | var options_index = indexFunction();
|
131 | if (state.sort == "unsorted") return options_index;
|
132 | var sorted_index;
|
133 | sorted_options.some(function(d, i) {
|
134 | if (d.options_index === options_index) {
|
135 | sorted_index = i;
|
136 | return true;
|
137 | }
|
138 | });
|
139 | return sorted_index;
|
140 | };
|
141 |
|
142 | control_obj.value = function(value) {
|
143 | if (value === undefined) return options[indexFunction()];
|
144 | var index = options.indexOf(value);
|
145 | if (index !== -1) indexFunction(index);
|
146 | return control_obj;
|
147 | };
|
148 |
|
149 | control_obj.on = function(event, callback) {
|
150 | if (event === "change") changeHandlers.push(callback.bind(control_obj));
|
151 | return control_obj;
|
152 | };
|
153 |
|
154 | control_obj.update = function() {
|
155 | getRemToPx();
|
156 | sorted_options = sortArray(options, state, getParser(), getFormatter());
|
157 | updateControls(sorted_options);
|
158 | return control_obj;
|
159 | };
|
160 |
|
161 | control_obj.trigger = function(event) {
|
162 | if (event === "change") callOnChangeCallbacks();
|
163 | return control_obj;
|
164 | };
|
165 |
|
166 | var isPlaying = function(is_playing) {
|
167 | if (is_playing === undefined) return state._is_playing_;
|
168 | state._is_playing_ = !!is_playing;
|
169 | if (!is_playing) indexFunction(current_index);
|
170 | };
|
171 |
|
172 | control_obj._isPlaying_ = isPlaying;
|
173 |
|
174 | return control_obj;
|
175 | }
|
176 |
|
177 |
|
178 | export default init;
|