diff --git a/examples/Basic/.prettierrc.js b/.prettierrc.js similarity index 55% rename from examples/Basic/.prettierrc.js rename to .prettierrc.js index 5c4de1a..0310300 100644 --- a/examples/Basic/.prettierrc.js +++ b/.prettierrc.js @@ -1,6 +1,5 @@ module.exports = { - bracketSpacing: false, - jsxBracketSameLine: true, + bracketSpacing: true, singleQuote: true, trailingComma: 'all', }; diff --git a/DefaultLabel.js b/DefaultLabel.js index 7901bae..a0db23c 100644 --- a/DefaultLabel.js +++ b/DefaultLabel.js @@ -7,7 +7,6 @@ const sliderRadius = 3; const width = 50; export default class DefaultLabel extends React.Component { static propTypes = { - oneMarkerValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), twoMarkerValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), @@ -18,7 +17,6 @@ export default class DefaultLabel extends React.Component { twoMarkerPressed: PropTypes.bool, }; - render() { const { oneMarkerValue, @@ -36,7 +34,7 @@ export default class DefaultLabel extends React.Component { @@ -49,7 +47,7 @@ export default class DefaultLabel extends React.Component { diff --git a/MultiSlider.js b/MultiSlider.js index 3fd869e..61c11fd 100755 --- a/MultiSlider.js +++ b/MultiSlider.js @@ -1,4 +1,5 @@ import React from 'react'; +import { Text } from 'react-native'; import { StyleSheet, @@ -13,6 +14,8 @@ import DefaultMarker from './DefaultMarker'; import DefaultLabel from './DefaultLabel'; import { createArray, valueToPosition, positionToValue } from './converters'; +const SLIDER_HEIGHT = 50; + export default class MultiSlider extends React.Component { static defaultProps = { values: [0], @@ -35,15 +38,22 @@ export default class MultiSlider extends React.Component { customLabel: DefaultLabel, markerOffsetX: 0, markerOffsetY: 0, + markerSize: 0, sliderLength: 280, onToggleOne: undefined, onToggleTwo: undefined, + stepsAs: [], + showSteps: false, + showStepMarkers: true, + showStepLabels: true, enabledOne: true, enabledTwo: true, allowOverlap: false, snapped: false, + smoothSnapped: false, vertical: false, minMarkerOverlapDistance: 0, + minMarkerOverlapStepDistance: 0, testID: undefined, renderTrack: undefined, }; @@ -51,15 +61,54 @@ export default class MultiSlider extends React.Component { constructor(props) { super(props); + if ( + this.props.minMarkerOverlapDistance > 0 && + this.props.minMarkerOverlapStepDistance > 0 + ) { + console.error( + 'You should provide either "minMarkerOverlapDistance" or "minMarkerOverlapStepDistance", not both. Expect unreliable results.', + ); + } + this.optionsArray = this.props.optionsArray || createArray(this.props.min, this.props.max, this.props.step); - this.stepLength = this.props.sliderLength / this.optionsArray.length; + this.stepLength = this.props.sliderLength / (this.optionsArray.length - 1); var initialValues = this.props.values.map(value => - valueToPosition(value, this.optionsArray, this.props.sliderLength), + valueToPosition( + value, + this.optionsArray, + this.props.sliderLength, + this.props.markerSize, + ), ); + var tempStepsAs = {}; + this.props.stepsAs.forEach(step => { + if (step?.index !== undefined) { + tempStepsAs[step?.index] = step; + } + }); + + this.stepsAs = {}; + this.optionsArray.forEach((ops, index) => { + if (tempStepsAs[index]) { + var step = tempStepsAs[index]; + this.stepsAs[index] = { + stepLabel: step?.stepLabel ? step.stepLabel : ops, + suffix: step?.suffix ? step.suffix : '', + prefix: step?.prefix ? step.prefix : '', + }; + } else { + this.stepsAs[index] = { + stepLabel: ops, + suffix: '', + prefix: '', + }; + } + }); + this.state = { pressedOne: true, valueOne: this.props.values[0], @@ -149,15 +198,18 @@ export default class MultiSlider extends React.Component { const unconfined = I18nManager.isRTL ? this.state.pastOne - accumDistance : accumDistance + this.state.pastOne; - var bottom = 0; + var bottom = this.props.markerSize / 2; var trueTop = this.state.positionTwo - (this.props.allowOverlap ? 0 : this.props.minMarkerOverlapDistance > 0 ? this.props.minMarkerOverlapDistance - : this.stepLength); - var top = trueTop === 0 ? 0 : trueTop || this.props.sliderLength; + : (this.props.minMarkerOverlapStepDistance || 1) * this.stepLength); + var top = + trueTop === 0 + ? 0 + : trueTop || this.props.sliderLength - this.props.markerSize / 2; var confined = unconfined < bottom ? bottom : unconfined > top ? top : unconfined; var slipDisplacement = this.props.touchDimensions.slipDisplacement; @@ -170,11 +222,13 @@ export default class MultiSlider extends React.Component { confined, this.optionsArray, this.props.sliderLength, + this.props.markerSize, ); var snapped = valueToPosition( value, this.optionsArray, this.props.sliderLength, + this.props.markerSize, ); this.setState({ positionOne: this.props.snapped ? snapped : confined, @@ -223,8 +277,8 @@ export default class MultiSlider extends React.Component { ? 0 : this.props.minMarkerOverlapDistance > 0 ? this.props.minMarkerOverlapDistance - : this.stepLength); - var top = this.props.sliderLength; + : (this.props.minMarkerOverlapStepDistance || 1) * this.stepLength); + var top = this.props.sliderLength - this.props.markerSize / 2; var confined = unconfined < bottom ? bottom : unconfined > top ? top : unconfined; var slipDisplacement = this.props.touchDimensions.slipDisplacement; @@ -237,11 +291,13 @@ export default class MultiSlider extends React.Component { confined, this.optionsArray, this.props.sliderLength, + this.props.markerSize, ); var snapped = valueToPosition( value, this.optionsArray, this.props.sliderLength, + this.props.markerSize, ); this.setState({ @@ -275,9 +331,16 @@ export default class MultiSlider extends React.Component { return; } + var snapped = valueToPosition( + this.state.valueOne, + this.optionsArray, + this.props.sliderLength, + ); + this.setState( { - pastOne: this.state.positionOne, + pastOne: this.props.smoothSnapped ? snapped : this.state.positionOne, + ...(this.props.smoothSnapped ? { positionOne: snapped } : {}), onePressed: !this.state.onePressed, }, () => { @@ -296,10 +359,17 @@ export default class MultiSlider extends React.Component { return; } + var snapped = valueToPosition( + this.state.valueTwo, + this.optionsArray, + this.props.sliderLength, + ); + this.setState( { twoPressed: !this.state.twoPressed, - pastTwo: this.state.positionTwo, + pastTwo: this.props.smoothSnapped ? snapped : this.state.positionTwo, + ...(this.props.smoothSnapped ? { positionTwo: snapped } : {}), }, () => { this.props.onValuesChangeFinish([ @@ -354,6 +424,7 @@ export default class MultiSlider extends React.Component { this.props.values[0], this.optionsArray, this.props.sliderLength, + this.props.markerSize, ); nextState.valueOne = this.props.values[0]; nextState.pastOne = positionOne; @@ -363,6 +434,7 @@ export default class MultiSlider extends React.Component { this.props.values[1], this.optionsArray, this.props.sliderLength, + this.props.markerSize, ); nextState.valueTwo = this.props.values[1]; nextState.pastTwo = positionTwo; @@ -372,6 +444,50 @@ export default class MultiSlider extends React.Component { } } + getSteps() { + const stepLength = this.props.sliderLength / (this.optionsArray.length - 1); + const textStyles = [ + styles.stepLabel, + this.props.stepLabelStyle, + ...(this.props.vertical ? [{ transform: [{ rotate: '90deg' }] }] : []), + ]; + const markerHeight = this.props?.trackStyle?.height || styles.track.height; + const markerStyles = [ + styles.stepMarker, + { + height: markerHeight, + width: markerHeight, + borderRadius: markerHeight / 2, + }, + this.props.stepMarkerStyle, + ]; + + return this.optionsArray.map((number, index) => { + var step = this.stepsAs[index]; + return ( + + {this.props.showStepMarkers && + index !== 0 && + index !== this.optionsArray.length - 1 && ( + + )} + {this.props.showStepLabels && ( + {`${step.prefix}${step.stepLabel}${step.suffix}`} + )} + + ); + }); + } + render() { const { positionOne, positionTwo } = this.state; const { @@ -410,6 +526,8 @@ export default class MultiSlider extends React.Component { } = this.props.touchDimensions; const touchStyle = { borderRadius: borderRadius || 0, + ...(height && { height }), + ...(width && { width }), }; const markerContainerOne = { @@ -419,14 +537,25 @@ export default class MultiSlider extends React.Component { const markerContainerTwo = { top: markerOffsetY - 24, - right: trackThreeLength - markerOffsetX - 24, + right: trackThreeLength + markerOffsetX - 24, }; - const containerStyle = [styles.container, this.props.containerStyle]; + const containerStyle = [ + styles.container, + { + height: SLIDER_HEIGHT, + width: sliderLength, + }, + this.props.containerStyle, + ]; if (this.props.vertical) { containerStyle.push({ - transform: [{ rotate: '-90deg' }], + transform: [ + { translateX: -sliderLength / 2 + SLIDER_HEIGHT / 2 }, + { rotate: '-90deg' }, + { translateX: -sliderLength / 2 + SLIDER_HEIGHT / 2 }, + ], }); } @@ -485,6 +614,7 @@ export default class MultiSlider extends React.Component { ]} /> )} + {this.props.showSteps && this.getSteps()} + {this.props.enableLabel && (