diff --git a/DefaultMarker.js b/DefaultMarker.js index 33f197d..6f70b92 100644 --- a/DefaultMarker.js +++ b/DefaultMarker.js @@ -66,10 +66,19 @@ const styles = StyleSheet.create({ shadowOpacity: 0.2, }, harmony: { - height: 12, - width: 12, - borderRadius: 12, - backgroundColor: '#0D8675', + height: 30, + width: 30, + borderRadius: 30, + borderWidth: 1, + borderColor: '#DDDDDD', + backgroundColor: '#FFFFFF', + shadowColor: '#000000', + shadowOffset: { + width: 0, + height: 3, + }, + shadowRadius: 1, + shadowOpacity: 0.2, }, default: { height: 12, @@ -88,11 +97,7 @@ const styles = StyleSheet.create({ width: 20, borderRadius: 20, }, - harmony: { - height: 20, - width: 20, - borderRadius: 20, - }, + harmony: {}, default: { height: 20, width: 20, diff --git a/MultiSlider.harmony.js b/MultiSlider.harmony.js new file mode 100644 index 0000000..f5b4a34 --- /dev/null +++ b/MultiSlider.harmony.js @@ -0,0 +1,778 @@ +import React from 'react'; +import { Text, Animated } from 'react-native'; + +import { + StyleSheet, + PanResponder, + View, + Platform, + Dimensions, + I18nManager, + ImageBackground, +} from 'react-native'; + +import DefaultMarker from './DefaultMarker'; +import DefaultLabel from './DefaultLabel'; +import { createArray, valueToPosition, positionToValue } from './converters'; + +export default class MultiSlider extends React.Component { + static defaultProps = { + values: [0], + onValuesChangeStart: () => {}, + onValuesChange: values => {}, + onValuesChangeFinish: values => {}, + onMarkersPosition: values => {}, + step: 1, + min: 0, + max: 10, + touchDimensions: { + height: 50, + width: 50, + borderRadius: 15, + slipDisplacement: 200, + }, + customMarker: DefaultMarker, + customMarkerLeft: DefaultMarker, + customMarkerRight: DefaultMarker, + 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: '', + }; + + 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 - 1); + + var initialValues = this.props.values.map(value => + 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], + valueTwo: this.props.values[1], + pastOne: initialValues[0], + pastTwo: initialValues[1], + positionOne: initialValues[0], + positionTwo: initialValues[1], + }; + + this.subscribePanResponder(); + } + + subscribePanResponder = () => { + var customPanResponder = (start, move, end) => { + return PanResponder.create({ + onStartShouldSetPanResponder: (evt, gestureState) => true, + onStartShouldSetPanResponderCapture: (evt, gestureState) => true, + onMoveShouldSetPanResponder: (evt, gestureState) => true, + onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, + onPanResponderGrant: (evt, gestureState) => start(), + onPanResponderMove: (evt, gestureState) => move(gestureState), + onPanResponderTerminationRequest: (evt, gestureState) => false, + onPanResponderRelease: (evt, gestureState) => end(gestureState), + onPanResponderTerminate: (evt, gestureState) => end(gestureState), + onShouldBlockNativeResponder: (evt, gestureState) => true, + }); + }; + + this._panResponderBetween = customPanResponder( + gestureState => { + this.startOne(gestureState); + this.startTwo(gestureState); + }, + gestureState => { + this.moveOne(gestureState); + this.moveTwo(gestureState); + }, + gestureState => { + this.endOne(gestureState); + this.endTwo(gestureState); + }, + ); + + this._panResponderOne = customPanResponder( + this.startOne, + this.moveOne, + this.endOne, + ); + this._panResponderTwo = customPanResponder( + this.startTwo, + this.moveTwo, + this.endTwo, + ); + }; + + startOne = () => { + if (this.props.enabledOne) { + this.props.onValuesChangeStart(); + this.setState({ + onePressed: !this.state.onePressed, + }); + } + }; + + startTwo = () => { + if (this.props.enabledTwo) { + this.props.onValuesChangeStart(); + this.setState({ + twoPressed: !this.state.twoPressed, + }); + } + }; + + moveOne = gestureState => { + if (!this.props.enabledOne) { + return; + } + + const accumDistance = this.props.vertical + ? -gestureState.dy + : gestureState.dx; + const accumDistanceDisplacement = this.props.vertical + ? gestureState.dx + : gestureState.dy; + + const unconfined = I18nManager.isRTL + ? this.state.pastOne - accumDistance + : accumDistance + this.state.pastOne; + var bottom = this.props.markerSize / 2; + var trueTop = + this.state.positionTwo - + (this.props.allowOverlap + ? 0 + : this.props.minMarkerOverlapDistance > 0 + ? this.props.minMarkerOverlapDistance + : (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; + + if ( + Math.abs(accumDistanceDisplacement) < slipDisplacement || + !slipDisplacement + ) { + var value = positionToValue( + 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, + }); + + if (value !== this.state.valueOne) { + this.setState( + { + valueOne: value, + }, + () => { + var change = [this.state.valueOne]; + if (this.state.valueTwo) { + change.push(this.state.valueTwo); + } + this.props.onValuesChange(change); + + this.props.onMarkersPosition([ + this.state.positionOne, + this.state.positionTwo, + ]); + }, + ); + } + } + }; + + moveTwo = gestureState => { + if (!this.props.enabledTwo) { + return; + } + + const accumDistance = this.props.vertical + ? -gestureState.dy + : gestureState.dx; + const accumDistanceDisplacement = this.props.vertical + ? gestureState.dx + : gestureState.dy; + + const unconfined = I18nManager.isRTL + ? this.state.pastTwo - accumDistance + : accumDistance + this.state.pastTwo; + var bottom = + this.state.positionOne + + (this.props.allowOverlap + ? 0 + : this.props.minMarkerOverlapDistance > 0 + ? this.props.minMarkerOverlapDistance + : (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; + + if ( + Math.abs(accumDistanceDisplacement) < slipDisplacement || + !slipDisplacement + ) { + var value = positionToValue( + confined, + this.optionsArray, + this.props.sliderLength, + this.props.markerSize, + ); + var snapped = valueToPosition( + value, + this.optionsArray, + this.props.sliderLength, + this.props.markerSize, + ); + + this.setState({ + positionTwo: this.props.snapped ? snapped : confined, + }); + + if (value !== this.state.valueTwo) { + this.setState( + { + valueTwo: value, + }, + () => { + this.props.onValuesChange([ + this.state.valueOne, + this.state.valueTwo, + ]); + + this.props.onMarkersPosition([ + this.state.positionOne, + this.state.positionTwo, + ]); + }, + ); + } + } + }; + + endOne = gestureState => { + if (gestureState.moveX === 0 && this.props.onToggleOne) { + this.props.onToggleOne(); + return; + } + + var snapped = valueToPosition( + this.state.valueOne, + this.optionsArray, + this.props.sliderLength, + ); + + this.setState( + { + pastOne: this.props.smoothSnapped ? snapped : this.state.positionOne, + ...(this.props.smoothSnapped ? { positionOne: snapped } : {}), + onePressed: !this.state.onePressed, + }, + () => { + var change = [this.state.valueOne]; + if (this.state.valueTwo) { + change.push(this.state.valueTwo); + } + this.props.onValuesChangeFinish(change); + }, + ); + }; + + endTwo = gestureState => { + if (gestureState.moveX === 0 && this.props.onToggleTwo) { + this.props.onToggleTwo(); + return; + } + + var snapped = valueToPosition( + this.state.valueTwo, + this.optionsArray, + this.props.sliderLength, + ); + + this.setState( + { + twoPressed: !this.state.twoPressed, + pastTwo: this.props.smoothSnapped ? snapped : this.state.positionTwo, + ...(this.props.smoothSnapped ? { positionTwo: snapped } : {}), + }, + () => { + this.props.onValuesChangeFinish([ + this.state.valueOne, + this.state.valueTwo, + ]); + }, + ); + }; + + componentDidUpdate(prevProps, prevState) { + const { + positionOne: prevPositionOne, + positionTwo: prevPositionTwo, + } = prevState; + + const { positionOne, positionTwo } = this.state; + + if ( + typeof positionOne === 'undefined' && + typeof positionTwo !== 'undefined' + ) { + return; + } + + if (positionOne !== prevPositionOne || positionTwo !== prevPositionTwo) { + this.props.onMarkersPosition([positionOne, positionTwo]); + } + + if (this.state.onePressed || this.state.twoPressed) { + return; + } + + let nextState = {}; + if ( + prevProps.min !== this.props.min || + prevProps.max !== this.props.max || + prevProps.step !== this.props.step || + prevProps.values[0] !== this.props.values[0] || + prevProps.sliderLength !== this.props.sliderLength || + prevProps.values[1] !== this.props.values[1] || + (prevProps.sliderLength !== this.props.sliderLength && + prevProps.values[1]) + ) { + this.optionsArray = + this.props.optionsArray || + createArray(this.props.min, this.props.max, this.props.step); + + this.stepLength = this.props.sliderLength / this.optionsArray.length; + + const positionOne = valueToPosition( + this.props.values[0], + this.optionsArray, + this.props.sliderLength, + this.props.markerSize, + ); + nextState.valueOne = this.props.values[0]; + nextState.pastOne = positionOne; + nextState.positionOne = positionOne; + + const positionTwo = valueToPosition( + this.props.values[1], + this.optionsArray, + this.props.sliderLength, + this.props.markerSize, + ); + nextState.valueTwo = this.props.values[1]; + nextState.pastTwo = positionTwo; + nextState.positionTwo = positionTwo; + + this.setState(nextState); + } + } + + 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 { + selectedStyle, + unselectedStyle, + sliderLength, + markerOffsetX, + markerOffsetY, + } = this.props; + const twoMarkers = this.props.values.length == 2; // when allowOverlap, positionTwo could be 0, identified as string '0' and throwing 'RawText 0 needs to be wrapped in ' error + + const trackOneLength = positionOne; + const trackOneStyle = twoMarkers + ? unselectedStyle + : selectedStyle || styles.selectedTrack; + const trackThreeLength = twoMarkers ? sliderLength - positionTwo : 0; + const trackThreeStyle = unselectedStyle; + const trackTwoLength = sliderLength - trackOneLength - trackThreeLength; + const trackTwoStyle = twoMarkers + ? selectedStyle || styles.selectedTrack + : unselectedStyle; + const Marker = this.props.customMarker; + + const MarkerLeft = this.props.customMarkerLeft; + const MarkerRight = this.props.customMarkerRight; + const isMarkersSeparated = this.props.isMarkersSeparated || false; + + const Label = this.props.customLabel; + + const { + slipDisplacement, + height, + width, + borderRadius, + } = this.props.touchDimensions; + const touchStyle = { + borderRadius: borderRadius || 0, + ...(height && { height }), + ...(width && { width }), + }; + + const markerContainerOne = { + top: markerOffsetY - 24, + left: trackOneLength + markerOffsetX - 24, + }; + + const markerContainerTwo = { + top: markerOffsetY - 24, + right: trackThreeLength + markerOffsetX - 24, + }; + + const containerStyle = [styles.container, this.props.containerStyle]; + + if (this.props.vertical) { + containerStyle.push({ + transform: [{ rotate: '-90deg' }], + }); + } + + const body = ( + + + + + {twoMarkers && ( + + )} + {this.props.showSteps && this.getSteps()} + sliderLength / 2 && styles.topMarkerContainer, + ]} + > + (this._markerOne = component)} + {...this._panResponderOne.panHandlers} + > + {isMarkersSeparated === false ? ( + + ) : ( + + )} + + + {twoMarkers && positionOne !== this.props.sliderLength && ( + + (this._markerTwo = component)} + {...this._panResponderTwo.panHandlers} + > + {isMarkersSeparated === false ? ( + + ) : ( + + )} + + + )} + + + ); + + return ( + + {this.props.enableLabel && ( + + ); + } +} + +const styles = StyleSheet.create({ + container: { + position: 'relative', + height: 50, + justifyContent: 'center', + }, + fullTrack: { + flexDirection: 'row', + }, + track: { + ...Platform.select({ + ios: { + height: 2, + borderRadius: 2, + backgroundColor: '#A7A7A7', + }, + android: { + height: 2, + backgroundColor: '#CECECE', + }, + web: { + height: 2, + borderRadius: 2, + backgroundColor: '#A7A7A7', + }, + harmony: { + height: 2, + borderRadius: 2, + backgroundColor: '#A7A7A7', + }, + default: { + height: 2, + backgroundColor: '#CECECE', + } + }), + }, + selectedTrack: { + ...Platform.select({ + ios: { + backgroundColor: '#095FFF', + }, + android: { + backgroundColor: '#0D8675', + }, + web: { + backgroundColor: '#095FFF', + }, + harmony: { + backgroundColor: '#095FFF', + }, + default: { + backgroundColor: '#0D8675', + } + }), + }, + markerContainer: { + position: 'absolute', + width: 48, + height: 48, + backgroundColor: 'transparent', + justifyContent: 'center', + alignItems: 'center', + }, + topMarkerContainer: { + zIndex: 1, + }, + touch: { + backgroundColor: 'transparent', + justifyContent: 'center', + alignItems: 'center', + }, + step: { + position: 'absolute', + marginLeft: -5, + }, + stepMarker: { + position: 'absolute', + left: 2, + width: 6, + height: 6, + backgroundColor: '#0000008c', + borderRadius: 3, + }, + stepLabel: { + position: 'absolute', + top: 15, + color: '#333', + }, +}); diff --git a/MultiSlider.js b/MultiSlider.js index 9f10b41..e6c7d67 100755 --- a/MultiSlider.js +++ b/MultiSlider.js @@ -1,777 +1,3 @@ -import React from 'react'; -import { Text, Animated } from 'react-native'; +import MultiSlider from "@ptomasroos/react-native-multi-slider"; -import { - StyleSheet, - PanResponder, - View, - Platform, - Dimensions, - I18nManager, - ImageBackground, -} from 'react-native'; - -import DefaultMarker from './DefaultMarker'; -import DefaultLabel from './DefaultLabel'; -import { createArray, valueToPosition, positionToValue } from './converters'; - -export default class MultiSlider extends React.Component { - static defaultProps = { - values: [0], - onValuesChangeStart: () => {}, - onValuesChange: values => {}, - onValuesChangeFinish: values => {}, - onMarkersPosition: values => {}, - step: 1, - min: 0, - max: 10, - touchDimensions: { - height: 50, - width: 50, - borderRadius: 15, - slipDisplacement: 200, - }, - customMarker: DefaultMarker, - customMarkerLeft: DefaultMarker, - customMarkerRight: DefaultMarker, - 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: '', - }; - - 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 - 1); - - var initialValues = this.props.values.map(value => - 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], - valueTwo: this.props.values[1], - pastOne: initialValues[0], - pastTwo: initialValues[1], - positionOne: initialValues[0], - positionTwo: initialValues[1], - }; - - this.subscribePanResponder(); - } - - subscribePanResponder = () => { - var customPanResponder = (start, move, end) => { - return PanResponder.create({ - onStartShouldSetPanResponder: (evt, gestureState) => true, - onStartShouldSetPanResponderCapture: (evt, gestureState) => true, - onMoveShouldSetPanResponder: (evt, gestureState) => true, - onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, - onPanResponderGrant: (evt, gestureState) => start(), - onPanResponderMove: (evt, gestureState) => move(gestureState), - onPanResponderTerminationRequest: (evt, gestureState) => false, - onPanResponderRelease: (evt, gestureState) => end(gestureState), - onPanResponderTerminate: (evt, gestureState) => end(gestureState), - onShouldBlockNativeResponder: (evt, gestureState) => true, - }); - }; - - this._panResponderBetween = customPanResponder( - gestureState => { - this.startOne(gestureState); - this.startTwo(gestureState); - }, - gestureState => { - this.moveOne(gestureState); - this.moveTwo(gestureState); - }, - gestureState => { - this.endOne(gestureState); - this.endTwo(gestureState); - }, - ); - - this._panResponderOne = customPanResponder( - this.startOne, - this.moveOne, - this.endOne, - ); - this._panResponderTwo = customPanResponder( - this.startTwo, - this.moveTwo, - this.endTwo, - ); - }; - - startOne = () => { - if (this.props.enabledOne) { - this.props.onValuesChangeStart(); - this.setState({ - onePressed: !this.state.onePressed, - }); - } - }; - - startTwo = () => { - if (this.props.enabledTwo) { - this.props.onValuesChangeStart(); - this.setState({ - twoPressed: !this.state.twoPressed, - }); - } - }; - - moveOne = gestureState => { - if (!this.props.enabledOne) { - return; - } - - const accumDistance = this.props.vertical - ? -gestureState.dy - : gestureState.dx; - const accumDistanceDisplacement = this.props.vertical - ? gestureState.dx - : gestureState.dy; - - const unconfined = I18nManager.isRTL - ? this.state.pastOne - accumDistance - : accumDistance + this.state.pastOne; - var bottom = this.props.markerSize / 2; - var trueTop = - this.state.positionTwo - - (this.props.allowOverlap - ? 0 - : this.props.minMarkerOverlapDistance > 0 - ? this.props.minMarkerOverlapDistance - : (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; - - if ( - Math.abs(accumDistanceDisplacement) < slipDisplacement || - !slipDisplacement - ) { - var value = positionToValue( - 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, - }); - - if (value !== this.state.valueOne) { - this.setState( - { - valueOne: value, - }, - () => { - var change = [this.state.valueOne]; - if (this.state.valueTwo) { - change.push(this.state.valueTwo); - } - this.props.onValuesChange(change); - - this.props.onMarkersPosition([ - this.state.positionOne, - this.state.positionTwo, - ]); - }, - ); - } - } - }; - - moveTwo = gestureState => { - if (!this.props.enabledTwo) { - return; - } - - const accumDistance = this.props.vertical - ? -gestureState.dy - : gestureState.dx; - const accumDistanceDisplacement = this.props.vertical - ? gestureState.dx - : gestureState.dy; - - const unconfined = I18nManager.isRTL - ? this.state.pastTwo - accumDistance - : accumDistance + this.state.pastTwo; - var bottom = - this.state.positionOne + - (this.props.allowOverlap - ? 0 - : this.props.minMarkerOverlapDistance > 0 - ? this.props.minMarkerOverlapDistance - : (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; - - if ( - Math.abs(accumDistanceDisplacement) < slipDisplacement || - !slipDisplacement - ) { - var value = positionToValue( - confined, - this.optionsArray, - this.props.sliderLength, - this.props.markerSize, - ); - var snapped = valueToPosition( - value, - this.optionsArray, - this.props.sliderLength, - this.props.markerSize, - ); - - this.setState({ - positionTwo: this.props.snapped ? snapped : confined, - }); - - if (value !== this.state.valueTwo) { - this.setState( - { - valueTwo: value, - }, - () => { - this.props.onValuesChange([ - this.state.valueOne, - this.state.valueTwo, - ]); - - this.props.onMarkersPosition([ - this.state.positionOne, - this.state.positionTwo, - ]); - }, - ); - } - } - }; - - endOne = gestureState => { - if (gestureState.moveX === 0 && this.props.onToggleOne) { - this.props.onToggleOne(); - return; - } - - var snapped = valueToPosition( - this.state.valueOne, - this.optionsArray, - this.props.sliderLength, - ); - - this.setState( - { - pastOne: this.props.smoothSnapped ? snapped : this.state.positionOne, - ...(this.props.smoothSnapped ? { positionOne: snapped } : {}), - onePressed: !this.state.onePressed, - }, - () => { - var change = [this.state.valueOne]; - if (this.state.valueTwo) { - change.push(this.state.valueTwo); - } - this.props.onValuesChangeFinish(change); - }, - ); - }; - - endTwo = gestureState => { - if (gestureState.moveX === 0 && this.props.onToggleTwo) { - this.props.onToggleTwo(); - return; - } - - var snapped = valueToPosition( - this.state.valueTwo, - this.optionsArray, - this.props.sliderLength, - ); - - this.setState( - { - twoPressed: !this.state.twoPressed, - pastTwo: this.props.smoothSnapped ? snapped : this.state.positionTwo, - ...(this.props.smoothSnapped ? { positionTwo: snapped } : {}), - }, - () => { - this.props.onValuesChangeFinish([ - this.state.valueOne, - this.state.valueTwo, - ]); - }, - ); - }; - - componentDidUpdate(prevProps, prevState) { - const { - positionOne: prevPositionOne, - positionTwo: prevPositionTwo, - } = prevState; - - const { positionOne, positionTwo } = this.state; - - if ( - typeof positionOne === 'undefined' && - typeof positionTwo !== 'undefined' - ) { - return; - } - - if (positionOne !== prevPositionOne || positionTwo !== prevPositionTwo) { - this.props.onMarkersPosition([positionOne, positionTwo]); - } - - if (this.state.onePressed || this.state.twoPressed) { - return; - } - - let nextState = {}; - if ( - prevProps.min !== this.props.min || - prevProps.max !== this.props.max || - prevProps.step !== this.props.step || - prevProps.values[0] !== this.props.values[0] || - prevProps.sliderLength !== this.props.sliderLength || - prevProps.values[1] !== this.props.values[1] || - (prevProps.sliderLength !== this.props.sliderLength && - prevProps.values[1]) - ) { - this.optionsArray = - this.props.optionsArray || - createArray(this.props.min, this.props.max, this.props.step); - - this.stepLength = this.props.sliderLength / this.optionsArray.length; - - const positionOne = valueToPosition( - this.props.values[0], - this.optionsArray, - this.props.sliderLength, - this.props.markerSize, - ); - nextState.valueOne = this.props.values[0]; - nextState.pastOne = positionOne; - nextState.positionOne = positionOne; - - const positionTwo = valueToPosition( - this.props.values[1], - this.optionsArray, - this.props.sliderLength, - this.props.markerSize, - ); - nextState.valueTwo = this.props.values[1]; - nextState.pastTwo = positionTwo; - nextState.positionTwo = positionTwo; - - this.setState(nextState); - } - } - - 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 { - selectedStyle, - unselectedStyle, - sliderLength, - markerOffsetX, - markerOffsetY, - } = this.props; - const twoMarkers = this.props.values.length == 2; // when allowOverlap, positionTwo could be 0, identified as string '0' and throwing 'RawText 0 needs to be wrapped in ' error - - const trackOneLength = positionOne; - const trackOneStyle = twoMarkers - ? unselectedStyle - : selectedStyle || styles.selectedTrack; - const trackThreeLength = twoMarkers ? sliderLength - positionTwo : 0; - const trackThreeStyle = unselectedStyle; - const trackTwoLength = sliderLength - trackOneLength - trackThreeLength; - const trackTwoStyle = twoMarkers - ? selectedStyle || styles.selectedTrack - : unselectedStyle; - const Marker = this.props.customMarker; - - const MarkerLeft = this.props.customMarkerLeft; - const MarkerRight = this.props.customMarkerRight; - const isMarkersSeparated = this.props.isMarkersSeparated || false; - - const Label = this.props.customLabel; - - const { - slipDisplacement, - height, - width, - borderRadius, - } = this.props.touchDimensions; - const touchStyle = { - borderRadius: borderRadius || 0, - ...(height && { height }), - ...(width && { width }), - }; - - const markerContainerOne = { - top: markerOffsetY - 24, - left: trackOneLength + markerOffsetX - 24, - }; - - const markerContainerTwo = { - top: markerOffsetY - 24, - right: trackThreeLength + markerOffsetX - 24, - }; - - const containerStyle = [styles.container, this.props.containerStyle]; - - if (this.props.vertical) { - containerStyle.push({ - transform: [{ rotate: '-90deg' }], - }); - } - - const body = ( - - - - - {twoMarkers && ( - - )} - {this.props.showSteps && this.getSteps()} - sliderLength / 2 && styles.topMarkerContainer, - ]} - > - (this._markerOne = component)} - {...this._panResponderOne.panHandlers} - > - {isMarkersSeparated === false ? ( - - ) : ( - - )} - - - {twoMarkers && positionOne !== this.props.sliderLength && ( - - (this._markerTwo = component)} - {...this._panResponderTwo.panHandlers} - > - {isMarkersSeparated === false ? ( - - ) : ( - - )} - - - )} - - - ); - - return ( - - {this.props.enableLabel && ( - - ); - } -} - -const styles = StyleSheet.create({ - container: { - position: 'relative', - height: 50, - justifyContent: 'center', - }, - fullTrack: { - flexDirection: 'row', - }, - track: { - ...Platform.select({ - ios: { - height: 2, - borderRadius: 2, - backgroundColor: '#A7A7A7', - }, - android: { - height: 2, - backgroundColor: '#CECECE', - }, - web: { - height: 2, - borderRadius: 2, - backgroundColor: '#A7A7A7', - }, - harmony: { - height: 2, - backgroundColor: '#CECECE', - }, - default: { - height: 2, - backgroundColor: '#CECECE', - } - }), - }, - selectedTrack: { - ...Platform.select({ - ios: { - backgroundColor: '#095FFF', - }, - android: { - backgroundColor: '#0D8675', - }, - web: { - backgroundColor: '#095FFF', - }, - harmony: { - backgroundColor: '#0D8675', - }, - default: { - backgroundColor: '#0D8675', - } - }), - }, - markerContainer: { - position: 'absolute', - width: 48, - height: 48, - backgroundColor: 'transparent', - justifyContent: 'center', - alignItems: 'center', - }, - topMarkerContainer: { - zIndex: 1, - }, - touch: { - backgroundColor: 'transparent', - justifyContent: 'center', - alignItems: 'center', - }, - step: { - position: 'absolute', - marginLeft: -5, - }, - stepMarker: { - position: 'absolute', - left: 2, - width: 6, - height: 6, - backgroundColor: '#0000008c', - borderRadius: 3, - }, - stepLabel: { - position: 'absolute', - top: 15, - color: '#333', - }, -}); +export default MultiSlider; \ No newline at end of file diff --git a/package.json b/package.json index c84999a..e118643 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "@react-native-oh-tpl/react-native-multi-slider", - "version": "2.2.2-0.0.1", + "version": "2.2.2-0.0.2", "description": "Android and iOS supported pure JS slider component with multiple markers for React Native", - "main": "MultiSlider.js", + "main": "MultiSlider", "types": "index.d.ts", "scripts": { "prettier": "prettier --write \"**/*.js\"" @@ -47,7 +47,7 @@ "prettier": "^1.14.3" }, "harmony": { - "alias": "react-native-multi-slider" + "alias": "@ptomasroos/react-native-multi-slider" }, "bugs": { "url": "https://github.com/ptomasroos/react-native-multi-slider/issues"