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.blue}; 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).isRequired, draggableTrack: PropTypes.bool, onTrackDrag: PropTypes.func, onTrackMouseDown: PropTypes.func.isRequired, percentages: PropTypes.objectOf(PropTypes.number).isRequired }; } /** * @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.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 ( { this.node = node; }} > {this.props.children} ); } }