UNPKG

15 kBJavaScriptView Raw
1import Decimal from './decimal';
2export const ERROR_MSG = {
3 [1 /* VALUE */]: 'The type of the "value" is illegal',
4 [2 /* INTERVAL */]: 'The prop "interval" is invalid, "(max - min)" must be divisible by "interval"',
5 [3 /* MIN */]: 'The "value" must be greater than or equal to the "min".',
6 [4 /* MAX */]: 'The "value" must be less than or equal to the "max".',
7 [5 /* ORDER */]: 'When "order" is false, the parameters "minRange", "maxRange", "fixed", "enabled" are invalid.',
8};
9/**
10 * Slider logic control center
11 *
12 * @export
13 * @class Control
14 */
15export default class Control {
16 constructor(options) {
17 this.dotsPos = []; // The position of each slider
18 this.dotsValue = []; // The value of each slider
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 /* ORDER */);
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 // Set the slider position
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 // Get value by position
68 getValueByPos(pos) {
69 let value = this.parsePos(pos);
70 // When included is true, the return value is the value of the nearest mark
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 // Sync slider position
84 syncDotsPos() {
85 this.dotsPos = this.dotsValue.map(v => this.parseValue(v));
86 }
87 /**
88 * Get all the marks
89 *
90 * @readonly
91 * @type {Mark[]}
92 * @memberof Control
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 * Get the most recent slider index by position
134 *
135 * @param {number} pos
136 * @returns {number}
137 * @memberof Control
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 * Get index by value
147 *
148 * @param {Value} value
149 * @returns {number}
150 * @memberof Control
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 * Get value by index
160 *
161 * @param {index} number
162 * @returns {Value}
163 * @memberof Control
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 * Set the position of a single slider
178 *
179 * @param {number} pos
180 * @param {number} index
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 * In fixed mode, get the position of all slider changes
202 *
203 * @param {number} changePos Change distance of a single slider
204 * @param {number} index slider index
205 * @returns {DotsPosChangeArray}
206 * @memberof Control
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 * In minRange/maxRange mode, get the position of all slider changes
222 *
223 * @param {number} pos position of a single slider
224 * @param {number} changePos Change distance of a single slider
225 * @param {number} index slider index
226 * @returns {DotsPosChangeArray}
227 * @memberof Control
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 // Determine if the two positions are within the legal interval
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 * Get a valid position by pos
274 *
275 * @param {number} pos
276 * @param {number} index
277 * @returns {{ pos: number, inRange: boolean }}
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 * Calculate the position of the slider by value
297 *
298 * @param {Value} val
299 * @returns {number}
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 /* MIN */);
309 return 0;
310 }
311 if (val > this.max) {
312 this.emitError(4 /* MAX */);
313 return 0;
314 }
315 if (typeof val !== 'number' || val !== val) {
316 this.emitError(1 /* VALUE */);
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 * Calculate the value by position
326 *
327 * @param {number} pos
328 * @returns {Value}
329 * @memberof Control
330 */
331 parsePos(pos) {
332 const index = Math.round(pos / this.gap);
333 return this.getValueByIndex(index);
334 }
335 /**
336 * Determine if the location is active
337 *
338 * @param {number} pos
339 * @returns {boolean}
340 * @memberof Control
341 */
342 isActiveByPos(pos) {
343 return this.processArray.some(([start, end]) => pos >= start && pos <= end);
344 }
345 /**
346 * Get each value
347 *
348 * @returns {Value[]}
349 * @memberof Control
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 * Calculate the distance of the range
365 * @param range number
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 * The total number of values
398 *
399 * @type {number}
400 * @memberof Control
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 /* INTERVAL */);
412 return 0;
413 }
414 return total;
415 }
416 // Distance between each value
417 get gap() {
418 return 100 / this.total;
419 }
420 // The minimum distance between the two sliders
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 // Maximum distance between the two sliders
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 * Sliding range of each slider
445 *
446 * @type {Array<[number, number]>}
447 * @memberof Control
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//# sourceMappingURL=control.js.map
\No newline at end of file