feat(ui-toolkit): remove slider

BREAKING
This commit is contained in:
Sara Vieira 2017-11-20 11:34:53 +00:00 committed by Sérgio Ramos
parent 0be8553e29
commit 48b9aef8cb
19 changed files with 0 additions and 1680 deletions

View File

@ -16,7 +16,6 @@ export { default as Divider } from './divider';
export { default as Editor } from './editor';
export { default as IconButton } from './icon-button';
export { default as StatusLoader } from './status-loader';
export { default as Slider } from './slider';
export { default as Breadcrumb, Item as BreadcrumbItem } from './breadcrumb';

View File

@ -1,17 +0,0 @@
### Double Range Slider
```jsx
const React = require('react');
const { default: Slider } = require('./slider');
<Slider
minValue={0.25}
maxValue={8}
step={0.25}
value={{ min: 4, max: 4 }}
onChangeComplete={value => console.log(value)}
onChange={value => console.log(value)}
>
vCPUs
</Slider>;
```

View File

@ -1,77 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import InputRange from './react-input-range';
import remcalc from 'remcalc';
import FormLabel from '../form/label';
const Label = styled(FormLabel)`
margin-bottom: ${remcalc(10)};
margin-top: ${remcalc(12)};
`;
class Slider extends Component {
constructor(props) {
super(props);
this.state = {
minValue: this.props.minValue,
maxValue: this.props.maxValue,
value: this.props.value
};
this.changeValue = this.changeValue.bind(this);
}
changeValue(value) {
this.setState({ value }, () => this.props.onChange(value));
}
render() {
const { minValue, maxValue, value } = this.state;
const { children, ...rest } = this.props;
return (
<div>
<Label>{children}</Label>
<InputRange
{...rest}
minValue={minValue}
maxValue={maxValue}
value={value}
onChange={value => this.changeValue(value)}
/>
</div>
);
}
}
Slider.propTypes = {
minValue: PropTypes.number,
maxValue: PropTypes.number,
step: PropTypes.number,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.shape()]),
onChangeComplete: PropTypes.func,
onChange: PropTypes.func,
formatLabel: PropTypes.func,
ariaLabelledby: PropTypes.string,
ariaControls: PropTypes.string,
disabled: PropTypes.bool,
draggableTrack: PropTypes.bool,
onChangeStart: PropTypes.func,
children: PropTypes.node,
greyed: PropTypes.bool
};
Slider.defaultProps = {
onChangeComplete: () => {},
onChange: () => {},
formatLabel: value =>
(value.toString().split('.')[1] || []).length > 3
? Math.round(value).toFixed(3)
: value,
onChangeStart: () => {},
step: 1
};
export default Slider;

View File

@ -1,3 +0,0 @@
import InputRange from './input-range/input-range';
export default InputRange;

View File

@ -1,730 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import * as valueTransformer from './value-transformer';
import rangePropType from './range-prop-type';
import valuePropType from './value-prop-type';
import Slider from './slider';
import Track from './track';
import { captialize, distanceTo, isDefined, isObject, length } from '../utils';
import styled from 'styled-components';
import remcalc from 'remcalc';
export const RangeStyled = styled.div`
position: relative;
min-height: ${remcalc(10)};
`;
/**
* A React component that allows users to input numeric values within a range
* by dragging its sliders.
*/
export default class InputRange extends Component {
/**
* @ignore
* @override
* @return {Object}
*/
static get propTypes() {
return {
ariaLabelledby: PropTypes.string,
ariaControls: PropTypes.string,
classNames: PropTypes.objectOf(PropTypes.string),
disabled: PropTypes.bool,
draggableTrack: PropTypes.bool,
formatLabel: PropTypes.func,
maxValue: rangePropType,
minValue: rangePropType,
name: PropTypes.string,
onChangeStart: PropTypes.func,
onChange: PropTypes.func,
onChangeComplete: PropTypes.func,
step: PropTypes.number,
value: valuePropType
};
}
/**
* @ignore
* @override
* @return {Object}
*/
static get defaultProps() {
return {
disabled: false,
maxValue: 10,
minValue: 0,
step: 1
};
}
/**
* @param {Object} props
* @param {string} [props.ariaLabelledby]
* @param {string} [props.ariaControls]
* @param {InputRangeClassNames} [props.classNames]
* @param {boolean} [props.disabled = false]
* @param {Function} [props.formatLabel]
* @param {number|Range} [props.maxValue = 10]
* @param {number|Range} [props.minValue = 0]
* @param {string} [props.name]
* @param {string} props.onChange
* @param {Function} [props.onChangeComplete]
* @param {Function} [props.onChangeStart]
* @param {number} [props.step = 1]
* @param {number|Range} props.value
*/
constructor(props) {
super(props);
/**
* @private
* @type {?number}
*/
this.startValue = null;
/**
* @private
* @type {?Component}
*/
this.node = null;
/**
* @private
* @type {?Component}
*/
this.trackNode = null;
/**
* @private
* @type {bool}
*/
this.isSliderDragging = false;
this.handleSliderDrag = this.handleSliderDrag.bind(this);
this.handleTrackDrag = this.handleTrackDrag.bind(this);
this.handleTrackMouseDown = this.handleTrackMouseDown.bind(this);
this.handleInteractionStart = this.handleInteractionStart.bind(this);
this.handleInteractionEnd = this.handleInteractionEnd.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleMouseUp = this.handleMouseUp.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
this.handleTouchStart = this.handleTouchStart.bind(this);
this.handleTouchEnd = this.handleTouchEnd.bind(this);
this.state = {
value: this.props.value
}
}
/**
* @ignore
* @override
* @return {void}
*/
componentWillUnmount() {
this.removeDocumentMouseUpListener();
this.removeDocumentTouchEndListener();
}
/**
* Return the bounding rect of the track
* @private
* @return {ClientRect}
*/
getTrackClientRect() {
return this.trackNode && this.trackNode.getClientRect();
}
/**
* Return the slider key closest to a point
* @private
* @param {Point} position
* @return {string}
*/
getKeyByPosition(position) {
const values = valueTransformer.getValueFromProps(
this.props,
this.isMultiValue()
);
const positions = valueTransformer.getPositionsFromValues(
values,
this.props.minValue,
this.props.maxValue,
this.getTrackClientRect()
);
if (this.isMultiValue()) {
const distanceToMin = distanceTo(position, positions.min);
const distanceToMax = distanceTo(position, positions.max);
if (distanceToMin < distanceToMax) {
return 'min';
}
}
return 'max';
}
/**
* Return all the slider keys
* @private
* @return {string[]}
*/
getKeys() {
if (this.isMultiValue()) {
return ['min', 'max'];
}
return ['max'];
}
/**
* Return true if the difference between the new and the current value is
* greater or equal to the step amount of the component
* @private
* @param {Range} values
* @return {boolean}
*/
hasStepDifference(values) {
const currentValues = valueTransformer.getValueFromProps(
this.props,
this.isMultiValue()
);
return (
length(values.min, currentValues.min) >= this.props.step ||
length(values.max, currentValues.max) >= this.props.step
);
}
/**
* Return true if the component accepts a min and max value
* @private
* @return {boolean}
*/
isMultiValue() {
return isObject(this.props.value);
}
/**
* Return true if the range is within the max and min value of the component
* @private
* @param {Range} values
* @return {boolean}
*/
isWithinRange(values) {
if (this.isMultiValue()) {
return (
values.min >= this.props.minValue &&
values.max <= this.props.maxValue &&
values.min < values.max
);
}
return (
values.max >= this.props.minValue && values.max <= this.props.maxValue
);
}
/**
* Return true if the new value should trigger a render
* @private
* @param {Range} values
* @return {boolean}
*/
shouldUpdate(values) {
return this.isWithinRange(values) && this.hasStepDifference(values);
}
/**
* Update the position of a slider
* @private
* @param {string} key
* @param {Point} position
* @return {void}
*/
updatePosition(key, position) {
const values = valueTransformer.getValueFromProps(
this.props,
this.isMultiValue()
);
const positions = valueTransformer.getPositionsFromValues(
values,
this.props.minValue,
this.props.maxValue,
this.getTrackClientRect()
);
positions[key] = position;
this.updatePositions(positions);
}
/**
* Update the positions of multiple sliders
* @private
* @param {Object} positions
* @param {Point} positions.min
* @param {Point} positions.max
* @return {void}
*/
updatePositions(positions) {
const values = {
min: valueTransformer.getValueFromPosition(
positions.min,
this.props.minValue,
this.props.maxValue,
this.getTrackClientRect()
),
max: valueTransformer.getValueFromPosition(
positions.max,
this.props.minValue,
this.props.maxValue,
this.getTrackClientRect()
)
};
const transformedValues = {
min: valueTransformer.getStepValueFromValue(values.min, this.props.step),
max: valueTransformer.getStepValueFromValue(values.max, this.props.step)
};
this.updateValues(transformedValues);
}
/**
* Update the value of a slider
* @private
* @param {string} key
* @param {number} value
* @return {void}
*/
updateValue(key, value) {
const values = valueTransformer.getValueFromProps(
this.props,
this.isMultiValue()
);
values[key] = value;
this.updateValues(values);
}
/**
* Update the values of multiple sliders
* @private
* @param {Range|number} values
* @return {void}
*/
updateValues(values) {
if (!this.shouldUpdate(values)) {
return;
}
this.props.onChange(this.isMultiValue() ? values : values.max);
}
/**
* Increment the value of a slider by key name
* @private
* @param {string} key
* @return {void}
*/
incrementValue(key) {
const values = valueTransformer.getValueFromProps(
this.props,
this.isMultiValue()
);
const value = values[key] + this.props.step;
this.updateValue(key, value);
}
/**
* Decrement the value of a slider by key name
* @private
* @param {string} key
* @return {void}
*/
decrementValue(key) {
const values = valueTransformer.getValueFromProps(
this.props,
this.isMultiValue()
);
const value = values[key] - this.props.step;
this.updateValue(key, value);
}
/**
* Listen to mouseup event
* @private
* @return {void}
*/
addDocumentMouseUpListener() {
this.removeDocumentMouseUpListener();
this.node.ownerDocument.addEventListener('mouseup', this.handleMouseUp);
}
/**
* Listen to touchend event
* @private
* @return {void}
*/
addDocumentTouchEndListener() {
this.removeDocumentTouchEndListener();
this.node.ownerDocument.addEventListener('touchend', this.handleTouchEnd);
}
/**
* Stop listening to mouseup event
* @private
* @return {void}
*/
removeDocumentMouseUpListener() {
this.node && this.node.ownerDocument.removeEventListener('mouseup', this.handleMouseUp);
}
/**
* Stop listening to touchend event
* @private
* @return {void}
*/
removeDocumentTouchEndListener() {
this.node.ownerDocument.removeEventListener(
'touchend',
this.handleTouchEnd
);
}
/**
* Handle any "mousemove" event received by the slider
* @private
* @param {SyntheticEvent} event
* @param {string} key
* @return {void}
*/
handleSliderDrag(event, key) {
if (this.props.disabled) {
return;
}
const position = valueTransformer.getPositionFromEvent(
event,
this.getTrackClientRect()
);
this.isSliderDragging = true;
requestAnimationFrame(() => this.updatePosition(key, position));
}
/**
* Handle any "mousemove" event received by the track
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleTrackDrag(event, prevEvent) {
if (
this.props.disabled ||
!this.props.draggableTrack ||
this.isSliderDragging
) {
return;
}
const { maxValue, minValue, value: { max, min } } = this.props;
const position = valueTransformer.getPositionFromEvent(
event,
this.getTrackClientRect()
);
const value = valueTransformer.getValueFromPosition(
position,
minValue,
maxValue,
this.getTrackClientRect()
);
const stepValue = valueTransformer.getStepValueFromValue(
value,
this.props.step
);
const prevPosition = valueTransformer.getPositionFromEvent(
prevEvent,
this.getTrackClientRect()
);
const prevValue = valueTransformer.getValueFromPosition(
prevPosition,
minValue,
maxValue,
this.getTrackClientRect()
);
const prevStepValue = valueTransformer.getStepValueFromValue(
prevValue,
this.props.step
);
const offset = prevStepValue - stepValue;
const transformedValues = {
min: min - offset,
max: max - offset
};
this.updateValues(transformedValues);
}
/**
* Handle any "mousedown" event received by the track
* @private
* @param {SyntheticEvent} event
* @param {Point} position
* @return {void}
*/
handleTrackMouseDown(event, position) {
if (this.props.disabled) {
return;
}
const { maxValue, minValue, value: { max, min } } = this.props;
event.preventDefault();
const value = valueTransformer.getValueFromPosition(
position,
minValue,
maxValue,
this.getTrackClientRect()
);
const stepValue = valueTransformer.getStepValueFromValue(
value,
this.props.step
);
if (!this.props.draggableTrack || stepValue > max || stepValue < min) {
this.updatePosition(this.getKeyByPosition(position), position);
}
}
/**
* Handle the start of any mouse/touch event
* @private
* @return {void}
*/
handleInteractionStart() {
if (this.props.onChangeStart) {
this.props.onChangeStart(this.props.value);
}
if (this.props.onChangeComplete && !isDefined(this.startValue)) {
this.startValue = this.props.value;
}
}
/**
* Handle the end of any mouse/touch event
* @private
* @return {void}
*/
handleInteractionEnd() {
if (this.isSliderDragging) {
this.isSliderDragging = false;
}
if (!this.props.onChangeComplete || !isDefined(this.startValue)) {
return;
}
if (this.startValue !== this.props.value) {
this.props.onChangeComplete(this.props.value);
}
this.startValue = null;
}
/**
* Handle any "keydown" event received by the component
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleKeyDown(event) {
this.handleInteractionStart(event);
}
/**
* Handle any "keyup" event received by the component
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleKeyUp(event) {
this.handleInteractionEnd(event);
}
/**
* Handle any "mousedown" event received by the component
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleMouseDown(event) {
this.handleInteractionStart(event);
this.addDocumentMouseUpListener();
}
/**
* Handle any "mouseup" event received by the component
* @private
* @param {SyntheticEvent} event
*/
handleMouseUp(event) {
this.handleInteractionEnd(event);
this.removeDocumentMouseUpListener();
}
/**
* Handle any "touchstart" event received by the component
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleTouchStart(event) {
this.handleInteractionStart(event);
this.addDocumentTouchEndListener();
}
/**
* Handle any "touchend" event received by the component
* @private
* @param {SyntheticEvent} event
*/
handleTouchEnd(event) {
this.handleInteractionEnd(event);
this.removeDocumentTouchEndListener();
}
/**
* Return JSX of sliders
* @private
* @return {JSX.Element}
*/
renderSliders() {
const values = valueTransformer.getValueFromProps(
this.props,
this.isMultiValue()
);
const percentages = valueTransformer.getPercentagesFromValues(
values,
this.props.minValue,
this.props.maxValue
);
return this.getKeys().map(key => {
const value = values[key];
const percentage = percentages[key];
let { maxValue, minValue } = this.props;
if (key === 'min') {
maxValue = values.max;
} else {
minValue = values.min;
}
const slider = (
<Slider
ariaLabelledby={this.props.ariaLabelledby}
ariaControls={this.props.ariaControls}
classNames={this.props.classNames}
formatLabel={this.props.formatLabel}
key={key}
greyed={this.props.greyed}
maxValue={maxValue}
minValue={minValue}
onSliderDrag={this.handleSliderDrag}
percentage={percentage}
type={key}
value={value}
/>
);
return slider;
});
}
/**
* Return JSX of hidden inputs
* @private
* @return {JSX.Element}
*/
renderHiddenInputs() {
if (!this.props.name) {
return [];
}
const isMultiValue = this.isMultiValue();
const values = valueTransformer.getValueFromProps(this.props, isMultiValue);
return this.getKeys().map(key => {
const value = values[key];
const name = isMultiValue
? `${this.props.name}${captialize(key)}`
: this.props.name;
return <input key={key} type="hidden" name={name} value={value} />;
});
}
/**
* @ignore
* @override
* @return {JSX.Element}
*/
render() {
const values = valueTransformer.getValueFromProps(
this.props,
this.isMultiValue()
);
const percentages = valueTransformer.getPercentagesFromValues(
values,
this.props.minValue,
this.props.maxValue
);
return (
<RangeStyled
aria-disabled={this.props.disabled}
innerRef={node => {
this.node = node;
}}
onKeyDown={this.handleKeyDown}
onKeyUp={this.handleKeyUp}
onMouseDown={this.handleMouseDown}
onTouchStart={this.handleTouchStart}
>
<Track
classNames={this.props.classNames}
draggableTrack={this.props.draggableTrack}
ref={trackNode => {
this.trackNode = trackNode;
}}
percentages={percentages}
onTrackDrag={this.handleTrackDrag}
onTrackMouseDown={this.handleTrackMouseDown}
>
{this.renderSliders()}
</Track>
{this.renderHiddenInputs()}
</RangeStyled>
);
}
}

View File

@ -1,48 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import remcalc from 'remcalc';
import theme from '../../../theme';
const Span = styled.span`
font-size: ${remcalc(13)};
position: absolute;
top: ${remcalc(14)};
right: ${props => (props.type === 'max' ? remcalc(1) : 'auto')};
color: ${props => (props.greyed ? theme.grey : theme.secondary)};
`;
/**
* @ignore
* @param {Object} props
* @param {InputRangeClassNames} props.classNames
* @param {Function} props.formatLabel
* @param {string} props.type
*/
export default function Label(props) {
const labelValue = props.formatLabel
? props.formatLabel(props.children, props.type)
: props.children;
return (
<Span greyed={props.greyed} type={props.type}>
{labelValue}
</Span>
);
}
/**
* @type {Object}
* @property {Function} children
* @property {Function} classNames
* @property {Function} formatLabel
* @property {Function} type
*/
Label.propTypes = {
children: PropTypes.node.isRequired,
classNames: PropTypes.objectOf(PropTypes.string),
formatLabel: PropTypes.func,
type: PropTypes.string,
greyed: PropTypes.bool
};

View File

@ -1,18 +0,0 @@
import { isNumber } from '../utils';
/**
* @ignore
* @param {Object} props - React component props
* @return {?Error} Return Error if validation fails
*/
export default function rangePropType(props) {
const { maxValue, minValue } = props;
if (!isNumber(minValue) || !isNumber(maxValue)) {
return new Error('"minValue" and "maxValue" must be a number');
}
if (minValue >= maxValue) {
return new Error('"minValue" must be smaller than "maxValue"');
}
}

View File

@ -1,315 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Label from './label';
import styled from 'styled-components';
import remcalc from 'remcalc';
import theme from '../../../theme';
export const SliderStyled = styled.div`
appearance: none;
background: ${theme.white};
border: ${remcalc(2)} solid ${theme.grey};
border-radius: 50%;
cursor: pointer;
display: block;
height: ${remcalc(14)};
width: ${remcalc(14)};
transform: translateY(-50%);
outline: none;
position: absolute;
top: 0;
margin-top: ${remcalc(2)};
&::active {
transform: scale(1.3);
}
&::focus {
box-shadow: 0 0 0 ${remcalc(5)} rgba(63, 81, 181, 0.2);
}
`;
/**
* @ignore
*/
export default class Slider extends Component {
/**
* Accepted propTypes of Slider
* @override
* @return {Object}
* @property {Function} ariaLabelledby
* @property {Function} ariaControls
* @property {Function} className
* @property {Function} formatLabel
* @property {Function} maxValue
* @property {Function} minValue
* @property {Function} onSliderDrag
* @property {Function} onSliderKeyDown
* @property {Function} percentage
* @property {Function} type
* @property {Function} value
*/
static get propTypes() {
return {
ariaLabelledby: PropTypes.string,
ariaControls: PropTypes.string,
classNames: PropTypes.objectOf(PropTypes.string),
formatLabel: PropTypes.func,
maxValue: PropTypes.number,
minValue: PropTypes.number,
onSliderDrag: PropTypes.func,
onSliderKeyDown: PropTypes.func,
percentage: PropTypes.number,
type: PropTypes.string,
value: PropTypes.number
};
}
/**
* @param {Object} props
* @param {string} [props.ariaLabelledby]
* @param {string} [props.ariaControls]
* @param {InputRangeClassNames} props.classNames
* @param {Function} [props.formatLabel]
* @param {number} [props.maxValue]
* @param {number} [props.minValue]
* @param {Function} props.onSliderKeyDown
* @param {Function} props.onSliderDrag
* @param {number} props.percentage
* @param {number} props.type
* @param {number} props.value
*/
constructor(props) {
super(props);
/**
* @private
* @type {?Component}
*/
this.node = null;
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleMouseUp = this.handleMouseUp.bind(this);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleTouchStart = this.handleTouchStart.bind(this);
this.handleTouchMove = this.handleTouchMove.bind(this);
this.handleTouchEnd = this.handleTouchEnd.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
}
/**
* @ignore
* @override
* @return {void}
*/
componentWillUnmount() {
this.removeDocumentMouseMoveListener();
this.removeDocumentMouseUpListener();
this.removeDocumentTouchEndListener();
this.removeDocumentTouchMoveListener();
}
/**
* @private
* @return {Object}
*/
getStyle() {
const percentage = (this.props.percentage || 0) * 100;
const style = {
position: 'absolute',
left: `${percentage > 94 ? 94 : percentage}%`,
};
return style;
}
/**
* Listen to mousemove event
* @private
* @return {void}
*/
addDocumentMouseMoveListener() {
this.removeDocumentMouseMoveListener();
this.node.ownerDocument.addEventListener('mousemove', this.handleMouseMove);
}
/**
* Listen to mouseup event
* @private
* @return {void}
*/
addDocumentMouseUpListener() {
this.removeDocumentMouseUpListener();
this.node.ownerDocument.addEventListener('mouseup', this.handleMouseUp);
}
/**
* Listen to touchmove event
* @private
* @return {void}
*/
addDocumentTouchMoveListener() {
this.removeDocumentTouchMoveListener();
this.node.ownerDocument.addEventListener('touchmove', this.handleTouchMove);
}
/**
* Listen to touchend event
* @private
* @return {void}
*/
addDocumentTouchEndListener() {
this.removeDocumentTouchEndListener();
this.node.ownerDocument.addEventListener('touchend', this.handleTouchEnd);
}
/**
* @private
* @return {void}
*/
removeDocumentMouseMoveListener() {
this.node.ownerDocument.removeEventListener(
'mousemove',
this.handleMouseMove
);
}
/**
* @private
* @return {void}
*/
removeDocumentMouseUpListener() {
this.node &&
this.node.ownerDocument.removeEventListener(
'mouseup',
this.handleMouseUp
);
}
/**
* @private
* @return {void}
*/
removeDocumentTouchMoveListener() {
this.node.ownerDocument.removeEventListener(
'touchmove',
this.handleTouchMove
);
}
/**
* @private
* @return {void}
*/
removeDocumentTouchEndListener() {
this.node.ownerDocument.removeEventListener(
'touchend',
this.handleTouchEnd
);
}
/**
* @private
* @return {void}
*/
handleMouseDown() {
this.addDocumentMouseMoveListener();
this.addDocumentMouseUpListener();
}
/**
* @private
* @return {void}
*/
handleMouseUp() {
this.removeDocumentMouseMoveListener();
this.removeDocumentMouseUpListener();
}
/**
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleMouseMove(event) {
this.props.onSliderDrag(event, this.props.type);
}
/**
* @private
* @return {void}
*/
handleTouchStart() {
this.addDocumentTouchEndListener();
this.addDocumentTouchMoveListener();
}
/**
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleTouchMove(event) {
this.props.onSliderDrag(event, this.props.type);
}
/**
* @private
* @return {void}
*/
handleTouchEnd() {
this.removeDocumentTouchMoveListener();
this.removeDocumentTouchEndListener();
}
/**
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleKeyDown(event) {
this.props.onSliderKeyDown(event, this.props.type);
}
/**
* @override
* @return {JSX.Element}
*/
render() {
const style = this.getStyle();
const props = this.props;
return (
<span
ref={node => {
this.node = node;
}}
>
<Label
greyed={props.greyed}
formatLabel={props.formatLabel}
type={props.type}
>
{props.value}
</Label>
<SliderStyled
type={props.type}
percentage={props.percentage}
style={style}
aria-labelledby={props.ariaLabelledby}
aria-controls={props.ariaControls}
aria-valuemax={props.maxValue}
aria-valuemin={props.minValue}
aria-valuenow={props.value}
draggable="false"
onKeyDown={this.handleKeyDown}
onMouseDown={this.handleMouseDown}
onTouchStart={this.handleTouchStart}
role="slider"
tabIndex="0"
/>
</span>
);
}
}

View File

@ -1,214 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import remcalc from 'remcalc';
import theme from '../../../theme';
export const TrackStyled = styled.div`
background: ${theme.grey};
cursor: pointer;
display: block;
height: ${remcalc(4)};
position: relative;
`;
const ActiveTrack = styled.div`
background: ${theme.primary};
height: 100%;
position: absolute;
`;
/**
* @ignore
*/
export default class Track extends Component {
/**
* @override
* @return {Object}
* @property {Function} children
* @property {Function} classNames
* @property {Boolean} draggableTrack
* @property {Function} onTrackDrag
* @property {Function} onTrackMouseDown
* @property {Function} percentages
*/
static get propTypes() {
return {
children: PropTypes.node.isRequired,
classNames: PropTypes.objectOf(PropTypes.string),
draggableTrack: PropTypes.bool,
onTrackDrag: PropTypes.func,
onTrackMouseDown: PropTypes.func,
percentages: PropTypes.objectOf(PropTypes.number)
};
}
/**
* @param {Object} props
* @param {InputRangeClassNames} props.classNames
* @param {Boolean} props.draggableTrack
* @param {Function} props.onTrackDrag
* @param {Function} props.onTrackMouseDown
* @param {number} props.percentages
*/
constructor(props) {
super(props);
/**
* @private
* @type {?Component}
*/
this.node = null;
this.trackDragEvent = null;
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleMouseUp = this.handleMouseUp.bind(this);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleTouchStart = this.handleTouchStart.bind(this);
}
/**
* @private
* @return {ClientRect}
*/
getClientRect() {
return this.node.getBoundingClientRect();
}
/**
* @private
* @return {Object} CSS styles
*/
getActiveTrackStyle() {
const width = `${(this.props.percentages.max - this.props.percentages.min) *
100}%`;
const left = `${this.props.percentages.min * 100}%`;
return { left, width };
}
/**
* Listen to mousemove event
* @private
* @return {void}
*/
addDocumentMouseMoveListener() {
this.removeDocumentMouseMoveListener();
this.node.ownerDocument.addEventListener('mousemove', this.handleMouseMove);
}
/**
* Listen to mouseup event
* @private
* @return {void}
*/
addDocumentMouseUpListener() {
this.removeDocumentMouseUpListener();
this.node.ownerDocument.addEventListener('mouseup', this.handleMouseUp);
}
/**
* @private
* @return {void}
*/
removeDocumentMouseMoveListener() {
this.node.ownerDocument.removeEventListener(
'mousemove',
this.handleMouseMove
);
}
/**
* @private
* @return {void}
*/
removeDocumentMouseUpListener() {
this.node &&
this.node.ownerDocument.removeEventListener(
'mouseup',
this.handleMouseUp
);
}
/**
* @private
* @param {SyntheticEvent} event
* @return {void}
*/
handleMouseMove(event) {
if (!this.props.draggableTrack) {
return;
}
if (this.trackDragEvent !== null) {
this.props.onTrackDrag(event, this.trackDragEvent);
}
this.trackDragEvent = event;
}
/**
* @private
* @return {void}
*/
handleMouseUp() {
if (!this.props.draggableTrack) {
return;
}
this.removeDocumentMouseMoveListener();
this.removeDocumentMouseUpListener();
this.trackDragEvent = null;
}
/**
* @private
* @param {SyntheticEvent} event - User event
*/
handleMouseDown(event) {
const clientX = event.touches ? event.touches[0].clientX : event.clientX;
const trackClientRect = this.getClientRect();
const position = {
x: clientX - trackClientRect.left,
y: 0
};
this.props.onTrackMouseDown(event, position);
if (this.props.draggableTrack) {
this.addDocumentMouseMoveListener();
this.addDocumentMouseUpListener();
}
}
/**
* @private
* @param {SyntheticEvent} event - User event
*/
handleTouchStart(event) {
event.preventDefault();
this.handleMouseDown(event);
}
/**
* @override
* @return {JSX.Element}
*/
render() {
const activeTrackStyle = this.getActiveTrackStyle();
return (
<TrackStyled
onMouseDown={this.handleMouseDown}
onTouchStart={this.handleTouchStart}
innerRef={node => {
this.node = node;
}}
>
<ActiveTrack style={activeTrackStyle} />
{this.props.children}
</TrackStyled>
);
}
}

View File

@ -1,36 +0,0 @@
import { isNumber, isObject } from '../utils';
/**
* @ignore
* @param {Object} props
* @return {?Error} Return Error if validation fails
*/
export default function valuePropType(props, propName) {
const { maxValue, minValue } = props;
const value = props[propName];
if (
!isNumber(value) &&
(!isObject(value) || !isNumber(value.min) || !isNumber(value.max))
) {
return new Error(`"${propName}" must be a number or a range object`);
}
if (isNumber(value) && (value < minValue || value > maxValue)) {
return new Error(
`"${propName}" must be in between "minValue" and "maxValue"`
);
}
if (
isObject(value) &&
(value.min < minValue ||
value.min > maxValue ||
value.max < minValue ||
value.max > maxValue)
) {
return new Error(
`"${propName}" must be in between "minValue" and "maxValue"`
);
}
}

View File

@ -1,144 +0,0 @@
import { clamp } from '../utils';
/**
* Convert a point into a percentage value
* @ignore
* @param {Point} position
* @param {ClientRect} clientRect
* @return {number} Percentage value
*/
export function getPercentageFromPosition(position, clientRect) {
const length = clientRect.width;
const sizePerc = position.x / length;
return sizePerc || 0;
}
/**
* Convert a point into a model value
* @ignore
* @param {Point} position
* @param {number} minValue
* @param {number} maxValue
* @param {ClientRect} clientRect
* @return {number}
*/
export function getValueFromPosition(position, minValue, maxValue, clientRect) {
const sizePerc = getPercentageFromPosition(position, clientRect);
const valueDiff = maxValue - minValue;
return minValue + valueDiff * sizePerc;
}
/**
* Convert props into a range value
* @ignore
* @param {Object} props
* @param {boolean} isMultiValue
* @return {Range}
*/
export function getValueFromProps(props, isMultiValue) {
if (isMultiValue) {
return { ...props.value };
}
return {
min: props.minValue,
max: props.value
};
}
/**
* Convert a model value into a percentage value
* @ignore
* @param {number} value
* @param {number} minValue
* @param {number} maxValue
* @return {number}
*/
export function getPercentageFromValue(value, minValue, maxValue) {
const validValue = clamp(value, minValue, maxValue);
const valueDiff = maxValue - minValue;
const valuePerc = (validValue - minValue) / valueDiff;
return valuePerc || 0;
}
/**
* Convert model values into percentage values
* @ignore
* @param {Range} values
* @param {number} minValue
* @param {number} maxValue
* @return {Range}
*/
export function getPercentagesFromValues(values, minValue, maxValue) {
return {
min: getPercentageFromValue(values.min, minValue, maxValue),
max: getPercentageFromValue(values.max, minValue, maxValue)
};
}
/**
* Convert a value into a point
* @ignore
* @param {number} value
* @param {number} minValue
* @param {number} maxValue
* @param {ClientRect} clientRect
* @return {Point} Position
*/
export function getPositionFromValue(value, minValue, maxValue, clientRect) {
const length = clientRect.width;
const valuePerc = getPercentageFromValue(value, minValue, maxValue);
const positionValue = valuePerc * length;
return {
x: positionValue,
y: 0
};
}
/**
* Convert a range of values into points
* @ignore
* @param {Range} values
* @param {number} minValue
* @param {number} maxValue
* @param {ClientRect} clientRect
* @return {Range}
*/
export function getPositionsFromValues(values, minValue, maxValue, clientRect) {
return {
min: getPositionFromValue(values.min, minValue, maxValue, clientRect),
max: getPositionFromValue(values.max, minValue, maxValue, clientRect)
};
}
/**
* Convert an event into a point
* @ignore
* @param {Event} event
* @param {ClientRect} clientRect
* @return {Point}
*/
export function getPositionFromEvent(event, clientRect) {
const length = clientRect.width;
const { clientX } = event.touches ? event.touches[0] : event;
return {
x: clamp(clientX - clientRect.left, 0, length),
y: 0
};
}
/**
* Convert a value into a step value
* @ignore
* @param {number} value
* @param {number} valuePerStep
* @return {number}
*/
export function getStepValueFromValue(value, valuePerStep) {
return Math.round(value / valuePerStep) * valuePerStep;
}

View File

@ -1,9 +0,0 @@
/**
* Captialize a string
* @ignore
* @param {string} string
* @return {string}
*/
export default function captialize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

View File

@ -1,11 +0,0 @@
/**
* Clamp a value between a min and max value
* @ignore
* @param {number} value
* @param {number} min
* @param {number} max
* @return {number}
*/
export default function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}

View File

@ -1,13 +0,0 @@
/**
* Calculate the distance between pointA and pointB
* @ignore
* @param {Point} pointA
* @param {Point} pointB
* @return {number} Distance
*/
export default function distanceTo(pointA, pointB) {
const xDiff = (pointB.x - pointA.x) ** 2;
const yDiff = (pointB.y - pointA.y) ** 2;
return Math.sqrt(xDiff + yDiff);
}

View File

@ -1,7 +0,0 @@
export { default as captialize } from './captialize';
export { default as clamp } from './clamp';
export { default as distanceTo } from './distance-to';
export { default as isDefined } from './is-defined';
export { default as isNumber } from './is-number';
export { default as isObject } from './is-object';
export { default as length } from './length';

View File

@ -1,9 +0,0 @@
/**
* Check if a value is defined
* @ignore
* @param {*} value
* @return {boolean}
*/
export default function isDefined(value) {
return value !== undefined && value !== null;
}

View File

@ -1,9 +0,0 @@
/**
* Check if a value is a number
* @ignore
* @param {*} value
* @return {boolean}
*/
export default function isNumber(value) {
return typeof value === 'number';
}

View File

@ -1,9 +0,0 @@
/**
* Check if a value is an object
* @ignore
* @param {*} value
* @return {boolean}
*/
export default function isObject(value) {
return value !== null && typeof value === 'object';
}

View File

@ -1,10 +0,0 @@
/**
* Calculate the absolute difference between two numbers
* @ignore
* @param {number} numA
* @param {number} numB
* @return {number}
*/
export default function length(numA, numB) {
return Math.abs(numA - numB);
}