painless-ui
Version:
238 lines (237 loc) • 10.6 kB
JavaScript
import React from 'react';
import { withTheme } from '../../providers/ThemeProvider';
import './Slider.css';
import Control from './Control';
const calculateValue = (percentage, max, min) => {
return Math.floor(((percentage - min) / 100) * (max - min)) + min;
};
const calculatePercentage = (value, max, min) => {
const diff = max - min;
return Math.floor(((value - min) / diff) * 100);
};
class Slider extends React.PureComponent {
constructor(props) {
super(props);
this.ref = null;
this.mainContainerRef = null;
this.onChangeCalledTimer = null;
this.lastCalled = 1;
this.state = {
position: calculatePercentage(this.props.value || this.props.min, this.props.max, this.props.min),
value: this.props.value || this.props.min,
position2: this.props.value || 0,
value2: this.props.value || this.props.min,
fromLocal: true
};
this.onChangeCallback = () => {
if (this.props.onChange) {
let value = this.state.value;
if (this.props.range) {
value = [value, this.state.value2];
}
this.props.onChange(value);
}
clearTimeout(this.onChangeCalledTimer);
this.onChangeCalledTimer = setTimeout(() => {
this.onChangeComplete();
}, 500);
};
this.onChangeComplete = () => {
if (this.props.onChangeComplete) {
let value = this.state.value;
if (this.props.range) {
value = [value, this.state.value2];
}
this.props.onChangeComplete(value);
}
};
this.dragHandler = (e) => {
let { percentage, value } = this.getValueAndPercentage(e);
if (percentage <= 100 && percentage >= 0 &&
(!this.props.range || percentage < this.state.position2)) {
if (this.props.step && value % this.props.step === 0) {
if (this.props.discrete) {
percentage = calculatePercentage(value, this.props.max, this.props.min);
}
this.setState({
position: percentage,
value: value,
fromLocal: true
}, this.onChangeCallback);
}
else {
this.setState({
position: percentage,
fromLocal: true
// value: value
}, this.onChangeCallback);
}
}
this.lastCalled = 1;
};
this.dragHandler2 = (e) => {
let { percentage, value } = this.getValueAndPercentage(e);
if (percentage <= 100 && percentage >= 0 &&
(!this.props.range || (percentage > this.state.position && this.state.value < value))) {
if (this.props.step && value % this.props.step === 0) {
if (this.props.discrete) {
percentage = calculatePercentage(value, this.props.max, this.props.min);
}
this.setState({
position2: percentage,
value2: value,
fromLocal: true
}, this.onChangeCallback);
}
else {
this.setState({
position2: percentage,
fromLocal: true
// value2: value * (this.props.step || 1)
}, this.onChangeCallback);
}
}
this.lastCalled = 2;
};
this.mouseUp = () => {
document.removeEventListener('mousemove', this.dragHandler, true);
document.removeEventListener('mousemove', this.dragHandler2, true);
if (this.state.value === 0 && this.state.position !== 0) {
this.setState({
position: 0,
fromLocal: true
});
}
};
if (props.range) {
setTimeout(() => {
this.setState({
position: calculatePercentage(this.props.value ? this.props.value[0] : this.props.min, this.props.max, this.props.min),
value: this.props.value ? this.props.value[0] : this.props.min,
position2: calculatePercentage(this.props.value ? this.props.value[1] : this.props.max, this.props.max, this.props.min),
value2: this.props.value ? this.props.value[1] : this.props.max,
fromLocal: true
});
});
}
}
getValueAndPercentage(e) {
const bar_width = this.mainContainerRef.offsetWidth;
let position = e.clientX - this.mainContainerRef.getBoundingClientRect().left;
if (position < 0) {
position = 0;
}
position = position + (this.props.step || 1);
const percentage = Math.round((position / bar_width) * 100);
let value = calculateValue(percentage + this.props.min, this.props.max, this.props.min);
if (this.props.step && this.props.step > value) {
value = 0;
}
return {
percentage,
value
};
}
getLeft() {
if (!this.props.range) {
return 0;
}
return this.state.position;
}
getWidth() {
if (!this.props.range) {
return this.state.position;
}
return this.state.position2 - this.state.position;
}
componentWillUnmount() {
document.removeEventListener('mousemove', this.dragHandler, true);
document.removeEventListener('mousemove', this.dragHandler2, true);
}
getGrid() {
const count = (this.props.max - this.props.min) / (this.props.step || 1);
const dataArray = [];
for (let i = 1; i < count; i++) {
dataArray.push((this.props.min) + ((this.props.step || 1) * i));
}
return dataArray;
}
static getDerivedStateFromProps(props, state) {
if (state.fromLocal === true) {
return {
fromLocal: false
};
}
if (props.value && props.range === false && props.value !== state.value) {
return {
value: props.value,
position: calculatePercentage(props.value ? props.value : props.min, props.max, props.min)
};
}
if (props.value && props.range === true && (props.value[0] !== state.value || props.value[1] !== state.value2)) {
return {
value: props.value[0],
value2: props.value[1],
position: calculatePercentage(props.value ? props.value[0] : props.min, props.max, props.min),
position2: calculatePercentage(props.value ? props.value[1] : props.max, props.max, props.min)
};
}
return null;
}
render() {
let defaultSliderStyle = {};
const theme = this.props.theme;
const colors = this.props.colors;
if (theme && theme.style) {
defaultSliderStyle = { ...defaultSliderStyle, ...theme.style };
}
let color = '#3f51b5';
let controlColor = '#3f51b5';
if (colors && colors.primary) {
color = colors.primary;
controlColor = colors.primary;
}
if (this.props.color) {
color = this.props.color;
}
if (this.props.controlColor) {
controlColor = this.props.controlColor;
}
let className = "ui-slider-container ";
if (this.props.className) {
className += this.props.className + " ";
}
if (theme && theme.className) {
className += theme.className;
}
const gridData = this.props.grid ? this.getGrid() : [];
return (React.createElement("div", { className: className },
React.createElement("div", { style: defaultSliderStyle, onMouseDown: () => {
this.mainContainerRef.ownerDocument.addEventListener('mouseup', this.mouseUp);
}, ref: (el) => this.mainContainerRef = el, className: "ui-slider" },
React.createElement("span", { className: "input-range__label input-range__label--min" },
React.createElement("span", { className: "input-range__label-container" }, this.props.min)),
React.createElement("div", { className: "input-range__track input-range__track--background", onClick: (e) => {
this.lastCalled === 1 ? this.dragHandler(e) : this.dragHandler2(e);
} },
React.createElement("div", { className: "input-range__track input-range__track--active", style: { "left": this.getLeft() + "%", width: this.getWidth() + "%", background: color } }),
React.createElement(Control, { color: controlColor, min: this.props.min, max: this.props.max, value: this.state.value, mouseUp: this.mouseUp, dragHandler: this.dragHandler, position: this.state.position, textColor: this.props.textColor }),
this.props.range ?
React.createElement(Control, { color: controlColor, min: this.props.min, max: this.props.max, value: this.state.value2, mouseUp: this.mouseUp, dragHandler: this.dragHandler2, position: this.state.position2, textColor: this.props.textColor }) : React.createElement(React.Fragment, null)),
React.createElement("span", { className: "input-range__label input-range__label--max" },
React.createElement("span", { className: "input-range__label-container" }, this.props.max))),
this.props.grid ?
React.createElement("ul", { className: "ui-slider-grid" }, gridData.map((n, i) => {
return React.createElement("li", { key: "grid-" + n + "-" + i, style: {
left: ((100 / (gridData.length + 1)) * (i + 1)) + "%"
} },
React.createElement("span", { className: "input-range__label" }, n));
})) : React.createElement(React.Fragment, null)));
}
}
Slider.defaultProps = {
min: 0,
max: 100,
step: 1
};
export default withTheme(Slider, 'Slider');