Skip to content

Commit 38eb810

Browse files
authored
Feature - Adding step labeling (ptomasroos#213)
* * Adding so its possible to show steps - Show step labels - Show step marks on the track - Customizable step labels, prefix and suffix styling et. - Customizable styling for step container, step label and step markers * Adding smooth-snapped * Added docs
1 parent 56325ec commit 38eb810

File tree

3 files changed

+138
-5
lines changed

3 files changed

+138
-5
lines changed

MultiSlider.js

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import { Text } from 'react-native';
23

34
import {
45
StyleSheet,
@@ -40,10 +41,15 @@ export default class MultiSlider extends React.Component {
4041
sliderLength: 280,
4142
onToggleOne: undefined,
4243
onToggleTwo: undefined,
44+
stepsAs: [],
45+
showSteps: false,
46+
showStepMarkers: true,
47+
showStepLabels: true,
4348
enabledOne: true,
4449
enabledTwo: true,
4550
allowOverlap: false,
4651
snapped: false,
52+
smoothSnapped: false,
4753
vertical: false,
4854
minMarkerOverlapDistance: 0,
4955
minMarkerOverlapStepDistance: 0,
@@ -53,8 +59,13 @@ export default class MultiSlider extends React.Component {
5359
constructor(props) {
5460
super(props);
5561

56-
if(this.props.minMarkerOverlapDistance > 0 && this.props.minMarkerOverlapStepDistance > 0) {
57-
console.error('You should provide either "minMarkerOverlapDistance" or "minMarkerOverlapStepDistance", not both. Expect unreliable results.');
62+
if (
63+
this.props.minMarkerOverlapDistance > 0 &&
64+
this.props.minMarkerOverlapStepDistance > 0
65+
) {
66+
console.error(
67+
'You should provide either "minMarkerOverlapDistance" or "minMarkerOverlapStepDistance", not both. Expect unreliable results.',
68+
);
5869
}
5970

6071
this.optionsArray =
@@ -71,6 +82,31 @@ export default class MultiSlider extends React.Component {
7182
),
7283
);
7384

85+
var tempStepsAs = {};
86+
this.props.stepsAs.forEach(step => {
87+
if (step?.index !== undefined) {
88+
tempStepsAs[step?.index] = step;
89+
}
90+
});
91+
92+
this.stepsAs = {};
93+
this.optionsArray.forEach((ops, index) => {
94+
if (tempStepsAs[index]) {
95+
var step = tempStepsAs[index];
96+
this.stepsAs[index] = {
97+
stepLabel: step?.stepLabel ? step.stepLabel : ops,
98+
suffix: step?.suffix ? step.suffix : '',
99+
prefix: step?.prefix ? step.prefix : '',
100+
};
101+
} else {
102+
this.stepsAs[index] = {
103+
stepLabel: ops,
104+
suffix: '',
105+
prefix: '',
106+
};
107+
}
108+
});
109+
74110
this.state = {
75111
pressedOne: true,
76112
valueOne: this.props.values[0],
@@ -293,9 +329,16 @@ export default class MultiSlider extends React.Component {
293329
return;
294330
}
295331

332+
var snapped = valueToPosition(
333+
this.state.valueOne,
334+
this.optionsArray,
335+
this.props.sliderLength,
336+
);
337+
296338
this.setState(
297339
{
298-
pastOne: this.state.positionOne,
340+
pastOne: this.props.smoothSnapped ? snapped : this.state.positionOne,
341+
...(this.props.smoothSnapped ? { positionOne: snapped } : {}),
299342
onePressed: !this.state.onePressed,
300343
},
301344
() => {
@@ -314,10 +357,17 @@ export default class MultiSlider extends React.Component {
314357
return;
315358
}
316359

360+
var snapped = valueToPosition(
361+
this.state.valueTwo,
362+
this.optionsArray,
363+
this.props.sliderLength,
364+
);
365+
317366
this.setState(
318367
{
319368
twoPressed: !this.state.twoPressed,
320-
pastTwo: this.state.positionTwo,
369+
pastTwo: this.props.smoothSnapped ? snapped : this.state.positionTwo,
370+
...(this.props.smoothSnapped ? { positionTwo: snapped } : {}),
321371
},
322372
() => {
323373
this.props.onValuesChangeFinish([
@@ -392,6 +442,50 @@ export default class MultiSlider extends React.Component {
392442
}
393443
}
394444

445+
getSteps() {
446+
const stepLength = this.props.sliderLength / (this.optionsArray.length - 1);
447+
const textStyles = [
448+
styles.stepLabel,
449+
this.props.stepLabelStyle,
450+
...(this.props.vertical ? [{ transform: [{ rotate: '90deg' }] }] : []),
451+
];
452+
const markerHeight = this.props?.trackStyle?.height || styles.track.height;
453+
const markerStyles = [
454+
styles.stepMarker,
455+
{
456+
height: markerHeight,
457+
width: markerHeight,
458+
borderRadius: markerHeight / 2,
459+
},
460+
this.props.stepMarkerStyle,
461+
];
462+
463+
return this.optionsArray.map((number, index) => {
464+
var step = this.stepsAs[index];
465+
return (
466+
<View
467+
key={number}
468+
style={[
469+
styles.step,
470+
this.props.stepStyle,
471+
{ left: stepLength * index },
472+
]}
473+
>
474+
{this.props.showStepMarkers &&
475+
index !== 0 &&
476+
index !== this.optionsArray.length - 1 && (
477+
<View style={markerStyles} />
478+
)}
479+
{this.props.showStepLabels && (
480+
<Text
481+
style={textStyles}
482+
>{`${step.prefix}${step.stepLabel}${step.suffix}`}</Text>
483+
)}
484+
</View>
485+
);
486+
});
487+
}
488+
395489
render() {
396490
const { positionOne, positionTwo } = this.state;
397491
const {
@@ -481,6 +575,7 @@ export default class MultiSlider extends React.Component {
481575
]}
482576
/>
483577
)}
578+
{this.props.showSteps && this.getSteps()}
484579
<View
485580
style={[
486581
styles.markerContainer,
@@ -648,4 +743,21 @@ const styles = StyleSheet.create({
648743
justifyContent: 'center',
649744
alignItems: 'center',
650745
},
746+
step: {
747+
position: 'absolute',
748+
marginLeft: -5,
749+
},
750+
stepMarker: {
751+
position: 'absolute',
752+
left: 2,
753+
width: 6,
754+
height: 6,
755+
backgroundColor: '#0000008c',
756+
borderRadius: 3,
757+
},
758+
stepLabel: {
759+
position: 'absolute',
760+
top: 15,
761+
color: '#333',
762+
},
651763
});

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,20 @@ Feel free to contribute to this part of the documentation.
8686
| max | 10 | number | Maximum value available in the slider. |
8787
| step | 1 | number | Step value of the slider. |
8888
| optionsArray | | array of numbers | Possible values of the slider. Ignores min and max. |
89-
| {container/track/selected/unselected/ markerContainer/marker/pressedMarker} Style | | style object | Styles for the slider |
89+
| {container/track/selected/unselected/ markerContainer/marker/pressedMarker/step/stepLabel/StepMarker} Style | | style object | Styles for the slider |
9090
| valuePrefix | | string | Prefix added to the value. |
9191
| valueSuffix | | string | Suffix added to the value. |
9292
| enabledOne | true | boolean | Enables the first cursor |
9393
| enabledTwo | true | boolean | Enables the second cursor |
94+
| stepsAs | [] | array of objects | Use stepsAs when you want to customize the steps-labels. stepsAs expects an array of objects [{index: number, stepLabel: string, prefix: string, suffix: string}]. Where index is for which step you want to customize, and all the other steps will show its index as its stepLabel. Both showSteps and showStepsLabels has to be enabled for stepsAs to be used. |
95+
| showSteps | false | boolean | Show steps |
96+
| showStepMarkers | true | boolean | Show steps-markers on the track, showSteps has to be enabled as well |
97+
| showStepLabels | true | boolean | Show steps-labels underneath the track, showSteps has to be enabled as well |
9498
| onToggleOne | undefined | function callback | Listener when first cursor toggles. |
9599
| onToggleTwo | undefined | function callback | Listener when second cursor toggles. |
96100
| allowOverlap | false | boolean | Allow the overlap within the cursors. |
97101
| snapped | false | boolean | Use this when you want a fixed position for your markers, this will split the slider in N specific positions |
102+
| smoothSnapped | false | boolean | Same as snapped but you can move the slider as usual. When released it will go to the nearest marker |
98103
| vertical | false | boolean | Use vertical orientation instead of horizontal. |
99104
| markerOffsetX | 0 | number | Offset the cursor(s) on the X axis |
100105
| markerOffsetY | 0 | number | Offset the cursor(s) on the Y axis |

index.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import * as React from "react";
77
import { ViewStyle } from "react-native";
88

99

10+
export interface StepsAsProps {
11+
index: number;
12+
stepLabel: string;
13+
prefix: string;
14+
suffix: string;
15+
}
16+
1017
export interface MarkerProps {
1118
pressed: boolean;
1219
pressedMarkerStyle: ViewStyle;
@@ -54,6 +61,8 @@ export interface MultiSliderProps {
5461
max?: number;
5562
step?: number;
5663

64+
stepsAs?: StepsAsProps[];
65+
5766
optionsArray?: number[];
5867

5968
containerStyle?: ViewStyle;
@@ -63,14 +72,21 @@ export interface MultiSliderProps {
6372
markerContainerStyle?: ViewStyle;
6473
markerStyle?: ViewStyle;
6574
pressedMarkerStyle?: ViewStyle;
75+
stepStyle?: ViewStyle;
76+
stepLabelStyle?: ViewStyle;
77+
stepMarkerStyle?: ViewStyle;
6678
valuePrefix?: string;
6779
valueSuffix?: string;
80+
showSteps?: boolean;
81+
showStepMarkers?: boolean;
82+
showStepLabels?: boolean;
6883
enabledOne?: boolean;
6984
enabledTwo?: boolean;
7085
onToggleOne?: () => void;
7186
onToggleTwo?: () => void;
7287
allowOverlap?: boolean;
7388
snapped?: boolean;
89+
smoothSnapped?: boolean;
7490
markerOffsetX?: number;
7591
markerOffsetY?: number;
7692
minMarkerOverlapDistance?: number;

0 commit comments

Comments
 (0)