1 | import Decimal from './decimal';
|
2 | export const ERROR_MSG = {
|
3 | [1 ]: 'The type of the "value" is illegal',
|
4 | [2 ]: 'The prop "interval" is invalid, "(max - min)" must be divisible by "interval"',
|
5 | [3 ]: 'The "value" must be greater than or equal to the "min".',
|
6 | [4 ]: 'The "value" must be less than or equal to the "max".',
|
7 | [5 ]: 'When "order" is false, the parameters "minRange", "maxRange", "fixed", "enabled" are invalid.',
|
8 | };
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | export default class Control {
|
16 | constructor(options) {
|
17 | this.dotsPos = [];
|
18 | this.dotsValue = [];
|
19 | this.cacheRangeDir = {};
|
20 | this.data = options.data;
|
21 | this.max = options.max;
|
22 | this.min = options.min;
|
23 | this.interval = options.interval;
|
24 | this.order = options.order;
|
25 | this.marks = options.marks;
|
26 | this.included = options.included;
|
27 | this.process = options.process;
|
28 | this.adsorb = options.adsorb;
|
29 | this.dotOptions = options.dotOptions;
|
30 | this.onError = options.onError;
|
31 | if (this.order) {
|
32 | this.minRange = options.minRange || 0;
|
33 | this.maxRange = options.maxRange || 0;
|
34 | this.enableCross = options.enableCross;
|
35 | this.fixed = options.fixed;
|
36 | }
|
37 | else {
|
38 | if (options.minRange || options.maxRange || !options.enableCross || options.fixed) {
|
39 | this.emitError(5 );
|
40 | }
|
41 | this.minRange = 0;
|
42 | this.maxRange = 0;
|
43 | this.enableCross = true;
|
44 | this.fixed = false;
|
45 | }
|
46 | this.setValue(options.value);
|
47 | }
|
48 | setValue(value) {
|
49 | this.setDotsValue(Array.isArray(value)
|
50 | ? this.order
|
51 | ? [...value].sort((a, b) => this.getIndexByValue(a) - this.getIndexByValue(b))
|
52 | : [...value]
|
53 | : [value], true);
|
54 | }
|
55 | setDotsValue(value, syncPos) {
|
56 | this.dotsValue = value;
|
57 | if (syncPos) {
|
58 | this.syncDotsPos();
|
59 | }
|
60 | }
|
61 |
|
62 | setDotsPos(dotsPos) {
|
63 | const list = this.order ? [...dotsPos].sort((a, b) => a - b) : dotsPos;
|
64 | this.dotsPos = list;
|
65 | this.setDotsValue(list.map(dotPos => this.getValueByPos(dotPos)), this.adsorb);
|
66 | }
|
67 |
|
68 | getValueByPos(pos) {
|
69 | let value = this.parsePos(pos);
|
70 |
|
71 | if (this.included) {
|
72 | let dir = 100;
|
73 | this.markList.forEach(mark => {
|
74 | const curDir = Math.abs(mark.pos - pos);
|
75 | if (curDir < dir) {
|
76 | dir = curDir;
|
77 | value = mark.value;
|
78 | }
|
79 | });
|
80 | }
|
81 | return value;
|
82 | }
|
83 |
|
84 | syncDotsPos() {
|
85 | this.dotsPos = this.dotsValue.map(v => this.parseValue(v));
|
86 | }
|
87 | |
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | get markList() {
|
95 | if (!this.marks) {
|
96 | return [];
|
97 | }
|
98 | const getMarkByValue = (value, mark) => {
|
99 | const pos = this.parseValue(value);
|
100 | return {
|
101 | pos,
|
102 | value,
|
103 | label: value,
|
104 | active: this.isActiveByPos(pos),
|
105 | ...mark,
|
106 | };
|
107 | };
|
108 | if (this.marks === true) {
|
109 | return this.getValues().map(value => getMarkByValue(value));
|
110 | }
|
111 | else if (Object.prototype.toString.call(this.marks) === '[object Object]') {
|
112 | return Object.keys(this.marks)
|
113 | .sort((a, b) => +a - +b)
|
114 | .map(value => {
|
115 | const item = this.marks[value];
|
116 | return getMarkByValue(value, typeof item !== 'string' ? item : { label: item });
|
117 | });
|
118 | }
|
119 | else if (Array.isArray(this.marks)) {
|
120 | return this.marks.map(value => getMarkByValue(value));
|
121 | }
|
122 | else if (typeof this.marks === 'function') {
|
123 | return this.getValues()
|
124 | .map(value => ({ value, result: this.marks(value) }))
|
125 | .filter(({ result }) => !!result)
|
126 | .map(({ value, result }) => getMarkByValue(value, result));
|
127 | }
|
128 | else {
|
129 | return [];
|
130 | }
|
131 | }
|
132 | |
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | getRecentDot(pos) {
|
140 | const arr = this.dotsPos
|
141 | .filter((dotPos, index) => !(this.getDotOption(index) && this.getDotOption(index).disabled))
|
142 | .map(dotPos => Math.abs(dotPos - pos));
|
143 | return arr.indexOf(Math.min(...arr));
|
144 | }
|
145 | |
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 | getIndexByValue(value) {
|
153 | if (this.data) {
|
154 | return this.data.indexOf(value);
|
155 | }
|
156 | return new Decimal(+value).minus(this.min).divide(this.interval).toNumber();
|
157 | }
|
158 | |
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | getValueByIndex(index) {
|
166 | if (index < 0) {
|
167 | index = 0;
|
168 | }
|
169 | else if (index > this.total) {
|
170 | index = this.total;
|
171 | }
|
172 | return this.data
|
173 | ? this.data[index]
|
174 | : new Decimal(index).multiply(this.interval).plus(this.min).toNumber();
|
175 | }
|
176 | |
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 | setDotPos(pos, index) {
|
183 | pos = this.getValidPos(pos, index).pos;
|
184 | const changePos = pos - this.dotsPos[index];
|
185 | if (!changePos) {
|
186 | return;
|
187 | }
|
188 | let changePosArr = new Array(this.dotsPos.length);
|
189 | if (this.fixed) {
|
190 | changePosArr = this.getFixedChangePosArr(changePos, index);
|
191 | }
|
192 | else if (this.minRange || this.maxRange) {
|
193 | changePosArr = this.getLimitRangeChangePosArr(pos, changePos, index);
|
194 | }
|
195 | else {
|
196 | changePosArr[index] = changePos;
|
197 | }
|
198 | this.setDotsPos(this.dotsPos.map((curPos, i) => curPos + (changePosArr[i] || 0)));
|
199 | }
|
200 | |
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 | getFixedChangePosArr(changePos, index) {
|
209 | this.dotsPos.forEach((originPos, i) => {
|
210 | if (i !== index) {
|
211 | const { pos: lastPos, inRange } = this.getValidPos(originPos + changePos, i);
|
212 | if (!inRange) {
|
213 | changePos =
|
214 | Math.min(Math.abs(lastPos - originPos), Math.abs(changePos)) * (changePos < 0 ? -1 : 1);
|
215 | }
|
216 | }
|
217 | });
|
218 | return this.dotsPos.map(_ => changePos);
|
219 | }
|
220 | |
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 | getLimitRangeChangePosArr(pos, changePos, index) {
|
230 | const changeDots = [{ index, changePos }];
|
231 | const newChangePos = changePos;
|
232 | [this.minRange, this.maxRange].forEach((isLimitRange, rangeIndex) => {
|
233 | if (!isLimitRange) {
|
234 | return false;
|
235 | }
|
236 | const isMinRange = rangeIndex === 0;
|
237 | const isForward = changePos > 0;
|
238 | let next = 0;
|
239 | if (isMinRange) {
|
240 | next = isForward ? 1 : -1;
|
241 | }
|
242 | else {
|
243 | next = isForward ? -1 : 1;
|
244 | }
|
245 |
|
246 | const inLimitRange = (pos2, pos1) => {
|
247 | const diff = Math.abs(pos2 - pos1);
|
248 | return isMinRange ? diff < this.minRangeDir : diff > this.maxRangeDir;
|
249 | };
|
250 | let i = index + next;
|
251 | let nextPos = this.dotsPos[i];
|
252 | let curPos = pos;
|
253 | while (this.isPos(nextPos) && inLimitRange(nextPos, curPos)) {
|
254 | const { pos: lastPos } = this.getValidPos(nextPos + newChangePos, i);
|
255 | changeDots.push({
|
256 | index: i,
|
257 | changePos: lastPos - nextPos,
|
258 | });
|
259 | i = i + next;
|
260 | curPos = lastPos;
|
261 | nextPos = this.dotsPos[i];
|
262 | }
|
263 | });
|
264 | return this.dotsPos.map((_, i) => {
|
265 | const changeDot = changeDots.filter(dot => dot.index === i);
|
266 | return changeDot.length ? changeDot[0].changePos : 0;
|
267 | });
|
268 | }
|
269 | isPos(pos) {
|
270 | return typeof pos === 'number';
|
271 | }
|
272 | |
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 | getValidPos(pos, index) {
|
280 | const range = this.valuePosRange[index];
|
281 | let inRange = true;
|
282 | if (pos < range[0]) {
|
283 | pos = range[0];
|
284 | inRange = false;
|
285 | }
|
286 | else if (pos > range[1]) {
|
287 | pos = range[1];
|
288 | inRange = false;
|
289 | }
|
290 | return {
|
291 | pos,
|
292 | inRange,
|
293 | };
|
294 | }
|
295 | |
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 | parseValue(val) {
|
302 | if (this.data) {
|
303 | val = this.data.indexOf(val);
|
304 | }
|
305 | else if (typeof val === 'number' || typeof val === 'string') {
|
306 | val = +val;
|
307 | if (val < this.min) {
|
308 | this.emitError(3 );
|
309 | return 0;
|
310 | }
|
311 | if (val > this.max) {
|
312 | this.emitError(4 );
|
313 | return 0;
|
314 | }
|
315 | if (typeof val !== 'number' || val !== val) {
|
316 | this.emitError(1 );
|
317 | return 0;
|
318 | }
|
319 | val = new Decimal(val).minus(this.min).divide(this.interval).toNumber();
|
320 | }
|
321 | const pos = new Decimal(val).multiply(this.gap).toNumber();
|
322 | return pos < 0 ? 0 : pos > 100 ? 100 : pos;
|
323 | }
|
324 | |
325 |
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 | parsePos(pos) {
|
332 | const index = Math.round(pos / this.gap);
|
333 | return this.getValueByIndex(index);
|
334 | }
|
335 | |
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 | isActiveByPos(pos) {
|
343 | return this.processArray.some(([start, end]) => pos >= start && pos <= end);
|
344 | }
|
345 | |
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 | getValues() {
|
352 | if (this.data) {
|
353 | return this.data;
|
354 | }
|
355 | else {
|
356 | const values = [];
|
357 | for (let i = 0; i <= this.total; i++) {
|
358 | values.push(new Decimal(i).multiply(this.interval).plus(this.min).toNumber());
|
359 | }
|
360 | return values;
|
361 | }
|
362 | }
|
363 | |
364 |
|
365 |
|
366 |
|
367 | getRangeDir(range) {
|
368 | return range
|
369 | ? new Decimal(range)
|
370 | .divide(new Decimal(this.data ? this.data.length - 1 : this.max)
|
371 | .minus(this.data ? 0 : this.min)
|
372 | .toNumber())
|
373 | .multiply(100)
|
374 | .toNumber()
|
375 | : 100;
|
376 | }
|
377 | emitError(type) {
|
378 | if (this.onError) {
|
379 | this.onError(type, ERROR_MSG[type]);
|
380 | }
|
381 | }
|
382 | get processArray() {
|
383 | if (this.process) {
|
384 | if (typeof this.process === 'function') {
|
385 | return this.process(this.dotsPos);
|
386 | }
|
387 | else if (this.dotsPos.length === 1) {
|
388 | return [[0, this.dotsPos[0]]];
|
389 | }
|
390 | else if (this.dotsPos.length > 1) {
|
391 | return [[Math.min(...this.dotsPos), Math.max(...this.dotsPos)]];
|
392 | }
|
393 | }
|
394 | return [];
|
395 | }
|
396 | |
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 | get total() {
|
403 | let total = 0;
|
404 | if (this.data) {
|
405 | total = this.data.length - 1;
|
406 | }
|
407 | else {
|
408 | total = new Decimal(this.max).minus(this.min).divide(this.interval).toNumber();
|
409 | }
|
410 | if (total - Math.floor(total) !== 0) {
|
411 | this.emitError(2 );
|
412 | return 0;
|
413 | }
|
414 | return total;
|
415 | }
|
416 |
|
417 | get gap() {
|
418 | return 100 / this.total;
|
419 | }
|
420 |
|
421 | get minRangeDir() {
|
422 | if (this.cacheRangeDir[this.minRange]) {
|
423 | return this.cacheRangeDir[this.minRange];
|
424 | }
|
425 | return (this.cacheRangeDir[this.minRange] = this.getRangeDir(this.minRange));
|
426 | }
|
427 |
|
428 | get maxRangeDir() {
|
429 | if (this.cacheRangeDir[this.maxRange])
|
430 | return this.cacheRangeDir[this.maxRange];
|
431 | return (this.cacheRangeDir[this.maxRange] = this.getRangeDir(this.maxRange));
|
432 | }
|
433 | getDotOption(index) {
|
434 | return Array.isArray(this.dotOptions) ? this.dotOptions[index] : this.dotOptions;
|
435 | }
|
436 | getDotRange(index, key, defaultValue) {
|
437 | if (!this.dotOptions) {
|
438 | return defaultValue;
|
439 | }
|
440 | const option = this.getDotOption(index);
|
441 | return option && option[key] !== void 0 ? this.parseValue(option[key]) : defaultValue;
|
442 | }
|
443 | |
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 | get valuePosRange() {
|
450 | const dotsPos = this.dotsPos;
|
451 | const valuePosRange = [];
|
452 | dotsPos.forEach((pos, i) => {
|
453 | valuePosRange.push([
|
454 | Math.max(this.minRange ? this.minRangeDir * i : 0, !this.enableCross ? dotsPos[i - 1] || 0 : 0, this.getDotRange(i, 'min', 0)),
|
455 | Math.min(this.minRange ? 100 - this.minRangeDir * (dotsPos.length - 1 - i) : 100, !this.enableCross ? dotsPos[i + 1] || 100 : 100, this.getDotRange(i, 'max', 100)),
|
456 | ]);
|
457 | });
|
458 | return valuePosRange;
|
459 | }
|
460 | get dotsIndex() {
|
461 | return this.dotsValue.map(val => this.getIndexByValue(val));
|
462 | }
|
463 | }
|
464 |
|
\ | No newline at end of file |