vue-slider-component
Version:
A highly customized slider component
464 lines • 15 kB
JavaScript
import Decimal from './decimal';
export const ERROR_MSG = {
[1 /* VALUE */]: 'The type of the "value" is illegal',
[2 /* INTERVAL */]: 'The prop "interval" is invalid, "(max - min)" must be divisible by "interval"',
[3 /* MIN */]: 'The "value" must be greater than or equal to the "min".',
[4 /* MAX */]: 'The "value" must be less than or equal to the "max".',
[5 /* ORDER */]: 'When "order" is false, the parameters "minRange", "maxRange", "fixed", "enabled" are invalid.',
};
/**
* Slider logic control center
*
* @export
* @class Control
*/
export default class Control {
constructor(options) {
this.dotsPos = []; // The position of each slider
this.dotsValue = []; // The value of each slider
this.cacheRangeDir = {};
this.data = options.data;
this.max = options.max;
this.min = options.min;
this.interval = options.interval;
this.order = options.order;
this.marks = options.marks;
this.included = options.included;
this.process = options.process;
this.adsorb = options.adsorb;
this.dotOptions = options.dotOptions;
this.onError = options.onError;
if (this.order) {
this.minRange = options.minRange || 0;
this.maxRange = options.maxRange || 0;
this.enableCross = options.enableCross;
this.fixed = options.fixed;
}
else {
if (options.minRange || options.maxRange || !options.enableCross || options.fixed) {
this.emitError(5 /* ORDER */);
}
this.minRange = 0;
this.maxRange = 0;
this.enableCross = true;
this.fixed = false;
}
this.setValue(options.value);
}
setValue(value) {
this.setDotsValue(Array.isArray(value)
? this.order
? [...value].sort((a, b) => this.getIndexByValue(a) - this.getIndexByValue(b))
: [...value]
: [value], true);
}
setDotsValue(value, syncPos) {
this.dotsValue = value;
if (syncPos) {
this.syncDotsPos();
}
}
// Set the slider position
setDotsPos(dotsPos) {
const list = this.order ? [...dotsPos].sort((a, b) => a - b) : dotsPos;
this.dotsPos = list;
this.setDotsValue(list.map(dotPos => this.getValueByPos(dotPos)), this.adsorb);
}
// Get value by position
getValueByPos(pos) {
let value = this.parsePos(pos);
// When included is true, the return value is the value of the nearest mark
if (this.included) {
let dir = 100;
this.markList.forEach(mark => {
const curDir = Math.abs(mark.pos - pos);
if (curDir < dir) {
dir = curDir;
value = mark.value;
}
});
}
return value;
}
// Sync slider position
syncDotsPos() {
this.dotsPos = this.dotsValue.map(v => this.parseValue(v));
}
/**
* Get all the marks
*
* @readonly
* @type {Mark[]}
* @memberof Control
*/
get markList() {
if (!this.marks) {
return [];
}
const getMarkByValue = (value, mark) => {
const pos = this.parseValue(value);
return {
pos,
value,
label: value,
active: this.isActiveByPos(pos),
...mark,
};
};
if (this.marks === true) {
return this.getValues().map(value => getMarkByValue(value));
}
else if (Object.prototype.toString.call(this.marks) === '[object Object]') {
return Object.keys(this.marks)
.sort((a, b) => +a - +b)
.map(value => {
const item = this.marks[value];
return getMarkByValue(value, typeof item !== 'string' ? item : { label: item });
});
}
else if (Array.isArray(this.marks)) {
return this.marks.map(value => getMarkByValue(value));
}
else if (typeof this.marks === 'function') {
return this.getValues()
.map(value => ({ value, result: this.marks(value) }))
.filter(({ result }) => !!result)
.map(({ value, result }) => getMarkByValue(value, result));
}
else {
return [];
}
}
/**
* Get the most recent slider index by position
*
* @param {number} pos
* @returns {number}
* @memberof Control
*/
getRecentDot(pos) {
const arr = this.dotsPos
.filter((dotPos, index) => !(this.getDotOption(index) && this.getDotOption(index).disabled))
.map(dotPos => Math.abs(dotPos - pos));
return arr.indexOf(Math.min(...arr));
}
/**
* Get index by value
*
* @param {Value} value
* @returns {number}
* @memberof Control
*/
getIndexByValue(value) {
if (this.data) {
return this.data.indexOf(value);
}
return new Decimal(+value).minus(this.min).divide(this.interval).toNumber();
}
/**
* Get value by index
*
* @param {index} number
* @returns {Value}
* @memberof Control
*/
getValueByIndex(index) {
if (index < 0) {
index = 0;
}
else if (index > this.total) {
index = this.total;
}
return this.data
? this.data[index]
: new Decimal(index).multiply(this.interval).plus(this.min).toNumber();
}
/**
* Set the position of a single slider
*
* @param {number} pos
* @param {number} index
*/
setDotPos(pos, index) {
pos = this.getValidPos(pos, index).pos;
const changePos = pos - this.dotsPos[index];
if (!changePos) {
return;
}
let changePosArr = new Array(this.dotsPos.length);
if (this.fixed) {
changePosArr = this.getFixedChangePosArr(changePos, index);
}
else if (this.minRange || this.maxRange) {
changePosArr = this.getLimitRangeChangePosArr(pos, changePos, index);
}
else {
changePosArr[index] = changePos;
}
this.setDotsPos(this.dotsPos.map((curPos, i) => curPos + (changePosArr[i] || 0)));
}
/**
* In fixed mode, get the position of all slider changes
*
* @param {number} changePos Change distance of a single slider
* @param {number} index slider index
* @returns {DotsPosChangeArray}
* @memberof Control
*/
getFixedChangePosArr(changePos, index) {
this.dotsPos.forEach((originPos, i) => {
if (i !== index) {
const { pos: lastPos, inRange } = this.getValidPos(originPos + changePos, i);
if (!inRange) {
changePos =
Math.min(Math.abs(lastPos - originPos), Math.abs(changePos)) * (changePos < 0 ? -1 : 1);
}
}
});
return this.dotsPos.map(_ => changePos);
}
/**
* In minRange/maxRange mode, get the position of all slider changes
*
* @param {number} pos position of a single slider
* @param {number} changePos Change distance of a single slider
* @param {number} index slider index
* @returns {DotsPosChangeArray}
* @memberof Control
*/
getLimitRangeChangePosArr(pos, changePos, index) {
const changeDots = [{ index, changePos }];
const newChangePos = changePos;
[this.minRange, this.maxRange].forEach((isLimitRange, rangeIndex) => {
if (!isLimitRange) {
return false;
}
const isMinRange = rangeIndex === 0;
const isForward = changePos > 0;
let next = 0;
if (isMinRange) {
next = isForward ? 1 : -1;
}
else {
next = isForward ? -1 : 1;
}
// Determine if the two positions are within the legal interval
const inLimitRange = (pos2, pos1) => {
const diff = Math.abs(pos2 - pos1);
return isMinRange ? diff < this.minRangeDir : diff > this.maxRangeDir;
};
let i = index + next;
let nextPos = this.dotsPos[i];
let curPos = pos;
while (this.isPos(nextPos) && inLimitRange(nextPos, curPos)) {
const { pos: lastPos } = this.getValidPos(nextPos + newChangePos, i);
changeDots.push({
index: i,
changePos: lastPos - nextPos,
});
i = i + next;
curPos = lastPos;
nextPos = this.dotsPos[i];
}
});
return this.dotsPos.map((_, i) => {
const changeDot = changeDots.filter(dot => dot.index === i);
return changeDot.length ? changeDot[0].changePos : 0;
});
}
isPos(pos) {
return typeof pos === 'number';
}
/**
* Get a valid position by pos
*
* @param {number} pos
* @param {number} index
* @returns {{ pos: number, inRange: boolean }}
*/
getValidPos(pos, index) {
const range = this.valuePosRange[index];
let inRange = true;
if (pos < range[0]) {
pos = range[0];
inRange = false;
}
else if (pos > range[1]) {
pos = range[1];
inRange = false;
}
return {
pos,
inRange,
};
}
/**
* Calculate the position of the slider by value
*
* @param {Value} val
* @returns {number}
*/
parseValue(val) {
if (this.data) {
val = this.data.indexOf(val);
}
else if (typeof val === 'number' || typeof val === 'string') {
val = +val;
if (val < this.min) {
this.emitError(3 /* MIN */);
return 0;
}
if (val > this.max) {
this.emitError(4 /* MAX */);
return 0;
}
if (typeof val !== 'number' || val !== val) {
this.emitError(1 /* VALUE */);
return 0;
}
val = new Decimal(val).minus(this.min).divide(this.interval).toNumber();
}
const pos = new Decimal(val).multiply(this.gap).toNumber();
return pos < 0 ? 0 : pos > 100 ? 100 : pos;
}
/**
* Calculate the value by position
*
* @param {number} pos
* @returns {Value}
* @memberof Control
*/
parsePos(pos) {
const index = Math.round(pos / this.gap);
return this.getValueByIndex(index);
}
/**
* Determine if the location is active
*
* @param {number} pos
* @returns {boolean}
* @memberof Control
*/
isActiveByPos(pos) {
return this.processArray.some(([start, end]) => pos >= start && pos <= end);
}
/**
* Get each value
*
* @returns {Value[]}
* @memberof Control
*/
getValues() {
if (this.data) {
return this.data;
}
else {
const values = [];
for (let i = 0; i <= this.total; i++) {
values.push(new Decimal(i).multiply(this.interval).plus(this.min).toNumber());
}
return values;
}
}
/**
* Calculate the distance of the range
* @param range number
*/
getRangeDir(range) {
return range
? new Decimal(range)
.divide(new Decimal(this.data ? this.data.length - 1 : this.max)
.minus(this.data ? 0 : this.min)
.toNumber())
.multiply(100)
.toNumber()
: 100;
}
emitError(type) {
if (this.onError) {
this.onError(type, ERROR_MSG[type]);
}
}
get processArray() {
if (this.process) {
if (typeof this.process === 'function') {
return this.process(this.dotsPos);
}
else if (this.dotsPos.length === 1) {
return [[0, this.dotsPos[0]]];
}
else if (this.dotsPos.length > 1) {
return [[Math.min(...this.dotsPos), Math.max(...this.dotsPos)]];
}
}
return [];
}
/**
* The total number of values
*
* @type {number}
* @memberof Control
*/
get total() {
let total = 0;
if (this.data) {
total = this.data.length - 1;
}
else {
total = new Decimal(this.max).minus(this.min).divide(this.interval).toNumber();
}
if (total - Math.floor(total) !== 0) {
this.emitError(2 /* INTERVAL */);
return 0;
}
return total;
}
// Distance between each value
get gap() {
return 100 / this.total;
}
// The minimum distance between the two sliders
get minRangeDir() {
if (this.cacheRangeDir[this.minRange]) {
return this.cacheRangeDir[this.minRange];
}
return (this.cacheRangeDir[this.minRange] = this.getRangeDir(this.minRange));
}
// Maximum distance between the two sliders
get maxRangeDir() {
if (this.cacheRangeDir[this.maxRange])
return this.cacheRangeDir[this.maxRange];
return (this.cacheRangeDir[this.maxRange] = this.getRangeDir(this.maxRange));
}
getDotOption(index) {
return Array.isArray(this.dotOptions) ? this.dotOptions[index] : this.dotOptions;
}
getDotRange(index, key, defaultValue) {
if (!this.dotOptions) {
return defaultValue;
}
const option = this.getDotOption(index);
return option && option[key] !== void 0 ? this.parseValue(option[key]) : defaultValue;
}
/**
* Sliding range of each slider
*
* @type {Array<[number, number]>}
* @memberof Control
*/
get valuePosRange() {
const dotsPos = this.dotsPos;
const valuePosRange = [];
dotsPos.forEach((pos, i) => {
valuePosRange.push([
Math.max(this.minRange ? this.minRangeDir * i : 0, !this.enableCross ? dotsPos[i - 1] || 0 : 0, this.getDotRange(i, 'min', 0)),
Math.min(this.minRange ? 100 - this.minRangeDir * (dotsPos.length - 1 - i) : 100, !this.enableCross ? dotsPos[i + 1] || 100 : 100, this.getDotRange(i, 'max', 100)),
]);
});
return valuePosRange;
}
get dotsIndex() {
return this.dotsValue.map(val => this.getIndexByValue(val));
}
}
//# sourceMappingURL=control.js.map