GithubHelp home page GithubHelp logo

tajo / react-range Goto Github PK

View Code? Open in Web Editor NEW
815.0 5.0 97.0 1.95 MB

🎚️Range input with a slider. Accessible. Bring your own styles and markup.

Home Page: https://react-range.netlify.app

License: MIT License

JavaScript 1.53% HTML 0.34% TypeScript 98.04% Shell 0.09%
range slider react component draggable

react-range's Introduction

react-range

npm version npm downloads Build Status size

Labeled Range

Edit react-range

See all the other examples and their source code!

Installation

yarn add react-range

Usage

import * as React from 'react';
import { Range } from 'react-range';

class SuperSimple extends React.Component {
  state = { values: [50] };
  render() {
    return (
      <Range
        step={0.1}
        min={0}
        max={100}
        values={this.state.values}
        onChange={(values) => this.setState({ values })}
        renderTrack={({ props, children }) => (
          <div
            {...props}
            style={{
              ...props.style,
              height: '6px',
              width: '100%',
              backgroundColor: '#ccc'
            }}
          >
            {children}
          </div>
        )}
        renderThumb={({ props }) => (
          <div
            {...props}
            style={{
              ...props.style,
              height: '42px',
              width: '42px',
              backgroundColor: '#999'
            }}
          />
        )}
      />
    );
  }
}

Features

  • Range input supporting vertical and horizontal sliding
  • Unopinionated styling, great for CSS in JS too
  • No wrapping divs or additional markup, bring your own!
  • Accessible, made for keyboards and screen readers
  • Touchable, works on mobile devices
  • Can handle negative and decimal values
  • Stateless and controlled single component
  • Typescript and Flow type definitions
  • No dependencies, less than 5kB (gzipped)
  • Coverage by e2e puppeteer tests
  • RTL support

Keyboard support

  • tab and shift+tab to focus thumbs
  • arrow up or arrow right or k to increase the thumb value by one step
  • arrow down or arrow left or j to decrease the thumb value by one step
  • page up to increase the thumb value by ten steps
  • page down to decrease the thumb value by ten steps

<Range /> props

renderTrack

renderTrack: (params: {
  props: {
    style: React.CSSProperties;
    ref: React.RefObject<any>;
    onMouseDown: (e: React.MouseEvent) => void;
    onTouchStart: (e: React.TouchEvent) => void;
  };
  children: React.ReactNode;
  isDragged: boolean;
  disabled: boolean;
}) => React.ReactNode;

renderTrack prop to define your track (root) element. Your function gets four parameters and should return a React component:

  • props - this needs to be spread over the root track element, it connects mouse and touch events, adds a ref and some necessary styling
  • children - the rendered thumbs, thumb structure should be specified in a different prop - renderThumb
  • isDragged - true if any thumb is being dragged
  • disabled - true if <Range disabled={true} /> is set

The track can be a single narrow div as in the Super simple example; however, it might be better to use at least two nested divs where the outer div is much thicker and has a transparent background and the inner div is narrow, has visible background and is centered. props should be then spread over the outer bigger div. Why to do this? It's nice to keep the onMouseDown and onTouchStart targets bigger since the thumb can be moved also by clicking on the track (in a single thumb scenario).

renderThumb

renderThumb: (params: {
  props: {
    key: number;
    style: React.CSSProperties;
    tabIndex?: number;
    'aria-valuemax': number;
    'aria-valuemin': number;
    'aria-valuenow': number;
    draggable: boolean;
    role: string;
    onKeyDown: (e: React.KeyboardEvent) => void;
    onKeyUp: (e: React.KeyboardEvent) => void;
  };
  value: number;
  index: number;
  isDragged: boolean;
}) => React.ReactNode;

renderThumb prop to define your thumb. Your function gets four parameters and should return a React component:

  • props - it has multiple props that you need to spread over your thumb element
  • value - a number, relative value based on min, max, step and the thumb's position
  • index - the thumb index (order)
  • isDragged - true if the thumb is dragged, great for styling purposes

renderMark (optional)

renderMark?: (params: {
  props: {
    key: string;
    style: React.CSSProperties;
    ref: React.RefObject<any>;
  };
  index: number;
}) => React.ReactNode;

renderMark is an optional prop so you can render an element at each step. See this example. Your function gets 2 parameters and should return a React component:

  • props - this needs to be spread over the root track element, it adds a ref, key and some necessary styling
  • index - index of the mark, might be useful if you want to use different styles for even/odd marks

You can use any dimensions for your marks and react-range will automatically position them at the correct place.

values

values: number[];

An array of numbers. It controls the position of thumbs on the track. values.length equals to the number of rendered thumbs.

onChange

onChange: (values: number[]) => void;

Called when a thumb is moved, provides new values.

onFinalChange

onFinalChange: (values: number[]) => void;

Called when a change is finished (mouse/touch up, or keyup), provides current values. Use this event when you have to make for example ajax request with new values.

min (optional)

min: number;

The range start. Can be decimal or negative. Default is 0.

max (optional)

max: number;

The range end. Can be decimal or negative. Default is 100.

step (optional)

step: number;

The minimal distance between two values. Can be decimal. Default is 1.

allowOverlap (optional)

allowOverlap: boolean;

When there are multiple thumbs on a single track, should they be allowed to overlap? Default is false.

draggableTrack (optional)

draggableTrack: boolean;

When there are multiple thumbs on a single track, should it be possible to drag all thumbs at once? Default is false.

direction (optional)

direction: Direction;

enum Direction {
  Right = 'to right',
  Left = 'to left',
  Down = 'to bottom',
  Up = 'to top'
}

It sets the orientation (vertical vs horizontal) and the direction in which the value increases. You can get this enum by:

import { Direction } from 'react-range';

Default value is Direction.Right.

disabled (optional)

disabled: boolean;

If true, it ignores all touch and mouse events and makes the component not focusable. Default is false.

rtl (optional)

rtl: boolean;

If true, the slider will be optimized for RTL layouts. Default is false.

getTrackBackground

There is an additional helper function being exported from react-range. Your track is most likely a div with some background. What if you want to achieve a nice "progress bar" effect where the part before the thumb has different color than the part after? What if you want to have the same thing even with multiple thumbs (aka differently colored segments)? You don't need to glue together multiple divs in order to do that! You can use a single div and set background: linear-gradient(...). getTrackBackground function builds this verbose linear-gradient(...) for you!

getTrackBackground: (params: {
  min: number;
  max: number;
  values: number[];
  colors: string[];
  direction?: Direction;
  rtl?: boolean;
}) => string;

min, max, values and direction should be same as for the <Range /> component. colors is a list of colors. This needs to be true:

values.length + 1 === colors.length;

That's because one thumb (one value) splits the track into two segments, so you need two colors.

Motivation

There is a native input solution:

<input type="range" />

However, it has some serious shortcomings:

  • vertical-oriented slider is not supported in all browsers
  • supports only a single direction
  • very limited styling options
  • no support for multiple thumbs

There are also many React based solutions but most of them are too bloated, don't support styling through CSS in JS or have lacking performance.

react-range has two main goals:

  • Small footprint - less then 4kB gzipped, single component.
  • Bring your own styles and HTML markup - react-range is a more low-level approach than other libraries. It doesn't come with any styling (except some positioning) or markup. It's up to the user to specify both! Think about react-range as a foundation for other styled input ranges.

End to end testing

This library is tightly coupled to many DOM APIs. It would be very hard to ensure 100% test coverage just with unit tests that would not involve a lot of mocking. Or we could re-architect the library to better abstract all DOM interfaces but that would mean more code and bigger footprint.

Instead of that, react-range adds thorough end to end tests powered by puppeteer.

All tests are automatically ran in Travis CI with headless chromium. This way, the public API is well tested, including pixel-perfect positioning. Also, the tests are pretty fast, reliable and very descriptive.

Do you want to run them in the dev mode (slows down operations, opens the browser)?

yarn ladle serve #start the ladle server
yarn test:e2e:dev #run the e2e tests

CI mode (ladle started on the background, quick, headless)

yarn test:e2e

Browser support

  • Chrome (latest, mac, windows, iOS, Android)
  • Firefox (latest, mac, windows)
  • Safari (latest, mac, iOS)
  • Edge (latest, windows)

Contributing

This is how you can spin up the dev environment:

git clone https://github.com/tajo/react-range
cd react-range
yarn
yarn ladle serve

Shoutouts 🙏

Big big shoutout to Tom MacWright for donating the react-range npm handle! ❤️

BrowserStack Logo

Big thanks to BrowserStack for letting the maintainers use their service to debug browser issues.

And Netlify for free hosting.

Author

Vojtech Miksu 2019, miksu.cz, @vmiksu

react-range's People

Contributors

0xflotus avatar adam-als avatar casdidier avatar cdashrowe avatar cdguglermbb avatar deliaz avatar dependabot[bot] avatar dfelber avatar dgershun avatar felix-kaestner avatar florianwiech avatar gergelyke avatar jh3y avatar josefzamrzla avatar jurajhrib avatar kantuni avatar khmm12 avatar leyanlo avatar lhaggar avatar maddeveloper avatar nadiia avatar rastalamm avatar skylude avatar tajo avatar tattali avatar thehaymaker avatar tomchentw avatar totalolage avatar vitalybaev avatar wafflepie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

react-range's Issues

Handling open ranges

First of all, thank you for creating this package(and react-movable etc.). The implementation quality is just mind blowing!

As per the issue, is there any plan to support open range values? Let me explain using my actual use case.

In my scenario, I have two requirements,

  1. The range should be 0-100
  2. There should be a way to represent "anything more than 100" and "anything less than 0"

I am not sure if this is feasible, nor if it's worth adding to this library. Let me know your thoughts on this.

Thanks!

IE 11 Merging labels layout not working

Hi, just observed that in IE 11 the Merging labels example is not correct.
The label is offset right and no labels are merging.

image

Is it a easy fix to make IE 11 work?

Initial position of thumbs is incorrect

The issue happens when the component, a time-picker first renders:
Screenshot 2020-03-05 at 13 52 49

Upon inspect, styles of the thumb appears to be:
Screenshot 2020-03-05 at 13 58 30

However, once the page is resized or once the thumb is clicked or dragged, the position will be corrected by itself.
Screenshot 2020-03-05 at 14 01 04

Any idea what might cause the issue? Thanks!

translateThumbs on container resize

Hi Tajo,

Cool library!

I'm trying to use react-range as one part of a project to replicate the Chrome audio player, which involves two range inputs (one for elapsed time, one for volume) which grow and shrink depending on which element the mouse hovers over.

Having rendered a component, when resizing the wrapping container the thumb position adjusts down proportionally to the width of the - but I can't work out how to invoke the inverse behaviour.

Any tips on how to solve this implementation issue?

Way to set language or custom text of error messages?

I am using react-range on a site that has both English and French. Is there a way to specify the language of the error messages (i.e. "Please enter a value larger than or equal to xx. Please enter a value between to xx and yy."), or set custom error text?

Add separate onUpdate() and onChange() events

AirBnb's rheostate offers onUpdate() and onChange() events, which is very clean. onUpdate() is used to set the state, such as values shown for the min and the max while dragging the slider. onChange() is used for 'committing' the value, and is where AJAX requests to an API would go.

I commented this, but creating an issue for easier tracking. Unfortunately had to jump back to rheostat because this was a blocker and it offers custom scales, had preferred react-range because it is so small in kb and seems to work well otherwise. Update: Just re-installed Rheostat, and React Range is so much smoother than Rheostat! Wish I could use it.

Thumbs and track position when resizing/zooming

Hi,
I've been facing an issue that I can reproduce in the Storybook of react-range (https://react-range.netlify.com/?path=/story/range--two-thumbs).
In my application, I have resizable panels and every time I resize it, my Sliders with two thumbs are displaced. It's the same behavior we have when we use the zoom-in/zoom-out for Storybook in the link above.
I would like to know if this is an issue and if this has some fix/workaround.
In my case, it happens only for Ranges with multiple thumbs. Ranges with a single thumb work nicely.

Thanks
Congratulations on the nice lib! :)

Is there a way to get to know when the range component is mounted?

first time when I am clicking on a react range component , I am getting a warning which says
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

I have tried adding a flag _isMounted from my end but the error is still coming..

{ this._isMounted && <Range values={this.props.distanceValue} step={STEP_DISTANCE} min={MIN_DISTANCE} max={MAX_DISTANCE} onChange={value => this.props.setDistanceState({ value })} renderTrack={({ props, children }) => ( <div onMouseDown={props.onMouseDown} onTouchStart={props.onTouchStart} style={{ ...props.style, height: "36px", display: "flex", width: "100%" }} > <div ref={props.ref} className="winds-blue-bg" style={{ height: "5px", width: "100%", borderRadius: "4px", background: getTrackBackground({ values: this.props.distanceValue, colors: ["#0078D7", "#ccc"], min: MIN_DISTANCE, max: MAX_DISTANCE }), alignSelf: "center" }} > {children} </div> </div> )} renderThumb={({ props, isDragged }) => ( <div {...props} style={{ ...props.style, height: "20px", width: "30px", borderRadius: "4px", backgroundColor: "#0078D7", display: "flex", justifyContent: "center", alignItems: "center", boxShadow: "0px 2px 6px #AAA" }} > <div style={{ position: "absolute", top: "0", color: "#fff", fontWeight: "bold", fontSize: "12px", borderRadius: "4px", backgroundColor: "#0078D7" }} > {this.props.distanceValue} </div> <div style={{ height: "16px", width: "5px", backgroundColor: isDragged ? "#0078D7" : "#0078D7" }} /> </div> )} /> }

Accessibility issues

  • Attribute valuenow doesn't match the output value. The first one eg return 54 while the output return 54.0

  • The current value is double announced since both valuenow and output are announced by SR with two different values (see first point). I suggest to add aria-hidden to the output element.

  • The sliders thumbs should move/snudge by "10" by pressing shift + arrows

  • The whole component should have a flag/prop to match the OS a11y preferences:

    • The component style should be dark when users enable the dark interfaces for eyes related disabilities. Using CSS @media prefers-color-scheme: dark
    • The component should not use animations when user enable Reduce motion. Using CSS @media prefers-reduced-motion: reduce

Feature request: Minimal distance between values

Hello, thanks for an awesome library!

I need to force the values to have minimal numeric distance between them - and it seems that it could be a nice feature for the library. So far I manipulate the value in onChange handler, but it is a bit laggy and I think the better place for doing so would be in the core, especially on utils/normalizeValue function.

Here's what I do (in simplified version):

const MAX = 100;
const MIN = 0;
const MIN_DISTANCE = 2;

const onChangeHandler = (values: number[]) => {
  return values.map((value: number) => {
    const prev = arr[i - 1];
    const next = arr[i + 1];
    let result = value;
    if (prev !== undefined && prev + MIN_DISTANCE > value) result = prev + MIN_DISTANCE;
    if (next !== undefined && next - MIN_DISTANCE < value) result = next - MIN_DISTANCE;
    return Math.max(Math.min(result, props.max || MAX), props.min || MIN);
  });
};

return <MyModifiedRange max={MAX} min={MIN} minDistance={MIN_DISTANCE} onChange={onChangeHandler} />

I'm not sure if that's the best approach to things (and that's why I did not lead with PR), but still seems that it would be a feature that would useful for more people.

Thanks!

ps: My use case was to chose preferred timeRange for a process of specific length. So if that length was 2 hours, values in that range could have been [10:00, 12:00] or [10:33, 12:33], but not [10:00, 11:00]

Allow click to change with two thumbs

It will be a nice enhancement to have the ability to change values with two thumbs.
I can make this done a bit later. What do you think?

I think it should be work this way:

  1. If click between thumbs - move the closest thumb to the mouse point
  2. If click before the first thumb - move it, else if click after the last thumb - move it. Of course, we should respect rtl.

Round step values to nearest value without depending on current state

I'm not sure whether to call this a suggestion/feature request/UX discussion, so here goes:

Imagine I have a range slider with min=1, max=4, and step=1.
Currently, when I move the slider, the value will change when I reach the next step value.
I would normally expect range sliders value to change to the nearest rounded step value.

This is my expectation from using Windows/Chrome/mouse, maybe on other OS/browser/pointer device combinations this behaves differently?

To clarify:

If I have currently selected 1 and I'm moving the slider from 1 to 4 (to the right):

|----|----|----|
1111122222333334
--- mouse position and value of the slider --->

If I have currently selected 4 and I'm moving the slider from 4 to 1 (to the left):

|----|----|----|
1222223333344444
<--- mouse position and value of the slider ---

What I would expect to happen (independent of the current slider value):

|----|----|----|
1112222233333444

What are your thoughts? Should this be the default behavior? Can there be an option (prop) to activate this behavior?

Z-Index of thumbs endlessly growing

This action occurs when you alternately click on different thumbs.
This can get in the way when you have a modal window with a small z-index and slider thumbs start getting on top of the overlay.

Would like the ability to focus thumb when clicked

What I can do today:

  1. Tab tab tab until I reach the thumb
  2. Use left/right arrows
    ...but step (1) is not a great user experience.

What I'd like to do:

  1. Click on thumb
  2. Use left/right arrows to move it

or

  1. Drag thumb
  2. Use left/right arrows to nudge it to the correct place with more precision

Would it be possible to focus the thumb when clicked?

Touch inputs only control first thumb (critical)

This bug prevents use of multi-thumb inputs on mobile.
When the input is a touch event, only the first thumb is consistently controllable. Tapping on the second (rightmost) thumb still controls the first thumb.
Tested on: Android Firefox, Android Chrome, Android Opera, Android Vivaldi
Sandbox: https://mvdm0.csb.app

Slider track background issue with multiple values and allow overlaping

Hi,

I am trying to get slider with 2 thumbs and with allow overlaping option.
Unfortunately, getTrackBackground function doesn't seem to be working correctly. As i overlap the background of the track is not filled with the middle color which i provide

I created a demo
It looks like a bug for me or maybe there is another way to workaround that issue.

Two Thumbs Stuck at Max Value

I noticed an issue using the range slider via baseweb actually, but digging around I realized this component being used under the hood exhibited the same behavior.

When two thumbs are shown and both moved to the maximum value, they are then stuck at that value, as the max slider cannot be moved to the left past the min slider, and it is rendered on top. Example below.

sliders-stuck

No touchStart removal on unmount

I noticed that on unmount, there is a duplicate here.

document.removeEventListener('mousedown', this.onMouseOrTouchStart as any);

Maybe you ment to remove the touchStart event ?

As a fact when we unmount the component on mobile the event remain here with no refs and raise the warning:

No thumbs found in the track container. Did you forget to pass & spread the props param in renderTrack?

Delay on thumb positioning

Hi,

When using React Range on certain occasions, such as when testing the component with Applitools, or exposing the component through an MDX file, is it possible see the moment when the translate CSS property gets applied to the THUMB and see it moving in the correct position. So we end up having a sort of 'animation' where the initial position of the thumb is the wrong one.

Of course, this is not an ideal behavior we would like to show to the user. I wonder if there is any fix you could do for it.

Thanks

Cannot read property 'getBoundingClientRect' of null

Hi

I am catching this error when I resize the browser window

Uncaught TypeError: Cannot read property 'getBoundingClientRect' of null at Range._this.getOffsets (Range.js:52) at Range._this.onResize (Range.js:157) at utils.js:151

Thumbs render out of place initially, only corrected in componentDidMount

Looks like it's measuring things in the browser using getBoundingClientRect and getComputedStyle, which only work after mounted, but that causes an incorrect initial render.

I'm working with large complex pages that are server-rendered first, so that initial render may persist for longer than acceptable.

Do you think it could be possible to get initial render to be correct? I'm wondering if that's achievable by passing some dimensional info in as props rather than just managing via custom CSS and measuring later. Could be tough given how flexible the rendering is, but wonder if you see a way to make that work.

Really like this component but don't think we can use it without a workaround for that.

Feature request: Custom layout of thumbs

I love the useThumbOverlap feature.

Is it possible to to support a thumb render function?
In most of my cases I would like to change the thumb to use, prefix, suffix or thousand seperator.

Step and decimals past the tenth place

Ideally, I would like the slider value to display more than 1 digit after the decimal.

Setting a step of .05 should allow me to scroll from 50.00 -> 50.05 -> 50.10 -> 50.15 -> 50.2. Instead there is some rounding going on and I am not able to view/step any decimals path the tenth place. I see 50.0 -> 50.1 -> 50.2

Default Import Error

Using import React from 'react' has led to issues with importing types definitions using ES2015 syntax behavior.
I understand it can be fixed by setting the esModuleInterop or allowSyntheticDefaultImports to true, but that's not an option for me because of the nature of our type configuration.

This could be fixed by updating the first line of types.d.ts file from:

  • import React from 'react';
    to
  • import * as React from 'react';
    react-range
    Thank you!

Remaining console.info in Range.js

I noticed in my console a console.info(thumbZIndexes) in the Range.js file on line 373. Is this intended?

I am using the 1.0.6 version of the awesome package :) !

Property 'Values' in conflict

Hey,

I am using react-range with a min / max of 0 and 1 and a step of 0.001. the values I am sending is within an array.

[0.66, 0.947]

I seem to get the warning appear on component load. Am I missing something as this seems in range to me..

Thanks in advance

merging labels when overlap set to false

Hey, I came across an issue when using the mergeing labels with a range that has allowOverlap set to false.

The merging label will show the value as one (ie 15) but when I inspect the value of the range (start, end) it is two different values

bug
Screenshot 2020-01-29 at 15 19 32

expected
Screenshot 2020-01-29 at 15 19 40

The code

import React, { useRef } from 'react';
import { Range, getTrackBackground, useThumbOverlap } from 'react-range';
import styled, { CSSProperties } from 'styled-components';

const CustomTrack = styled.div`
  height: 8px;
  width: 100%;
  border-radius: 4px;
  align-self: center;
  position: relative;

  &::before {
    content: '';
    content: '';
    width: 100%;
    height: 2px;
    background-color: #dfe7eb;
    top: 2px;
    position: absolute;
    z-index: -1;
  }
`;

const CustomThumb = styled.div`
  height: 16px;
  width: 16px;
  border-radius: 50%;
  background-color: #778293;
  display: flex;
  justify-content: center;
  align-items: center;
`;

const ThumbLabelStyled = styled.div`
  position: absolute;
  top: -18px;
  color: #1b2f37;
  font-size: 14px;
  white-space: nowrap;
`;

const ThumbLabel = ({
  rangeRef,
  values,
  index,
}: {
  rangeRef: Range | null;
  values: number[];
  index: number;
}) => {
  const [labelValue, style] = useThumbOverlap(rangeRef, values, index, 1);

  return (
    <ThumbLabelStyled data-label={index} style={style as CSSProperties}>
      {labelValue}
    </ThumbLabelStyled>
  );
};

interface Props {
  start: number;
  end: number;
  onChange: (values: number[]) => void;
}

const DecisionSlider: React.FunctionComponent<Props> = ({
  start,
  end,
  onChange,
}) => {
  const rangeRef = useRef(null);

  return (
    <Range
      values={[start, end]}
      step={1}
      min={0}
      max={100}
      onChange={onChange}
      ref={rangeRef}
      allowOverlap={false}
      renderTrack={({ props, children }: any) => (
        <div
          onMouseDown={props.onMouseDown}
          onTouchStart={props.onTouchStart}
          style={{
            ...props.style,
          }}
        >
          <CustomTrack
            ref={props.ref}
            style={{
              background: getTrackBackground({
                values: [start, end],
                colors: ['transparent', '#CFDBE1', 'transparent'],
                min: 0,
                max: 100,
              }),
            }}
          >
            {children}
          </CustomTrack>
        </div>
      )}
      renderThumb={({ props, index }) => {
        return (
          <CustomThumb
            {...props}
            style={{
              ...props.style,
            }}
          >
            <ThumbLabel
              rangeRef={rangeRef.current}
              values={[start, end]}
              index={index}
            />
          </CustomThumb>
        );
      }}
    />
  );
};

export default DecisionSlider;

Is it something I am doing wrong or is it a bug?

Controlling tabIndex of rendered thumb

Hey again @tajo - it seems like the only way I can have a tabIndex which is not 0 on the thumb is to disable the element. Would you be open to a prop for setting tabIndex?

Something like:
tabIndex: disabled ? undefined : customTabIndex ? customTabIndex : 0

Feature Request: Input boxes

I wondered if there might be a way to add custom input boxes to the thumbs of react range? could this be added to the renderthumb? Is there a way already coded in to include any mouse interactions double clicks for instance? I see an isDragged do we also get isDoubleClicked? I would like to link these two things togther

Step doesn't respect min/max values

You can duplicate this in the BigSteps example. Set MIN = 3, MAX = 103, and in the state, set values: [53]. The slider handle will render at 53 but the first drag right will go to 60, refresh and first drag left goes to 40.

What I'd expect to happen in this scenario is that each step would go by values of 10 from the starting value and respect the min/max, so right would be 63, 73, and so on, while left would be 43, 33, and so on.

It would also be good to add clamping to the values. For instance, if my min/max is 10/30 respectively, and my value is 5, it should default to 10. If its 40, it should default to 30. Here is what I've started to implement to get around this (in a wrapper I'm writing for this component), but now that I can't get the step to respect min/max values when dragging I'm stuck.

In my component I set the values that I'll pass to Range first:

const [values, setValues] = useState(() =>
    validateValues({defaultValues, min, max, step})
);

The above runs this:

// guard against any values that may be outside of the specified min/max range and make sure step aligns
function validateValues({defaultValues, min, max, step}) {
    const totalSteps = Math.floor((max - min) / step);

    return defaultValues.map(val => {
        const steps = Math.min((val - min) / step, totalSteps);
        const newVal = Math.round(steps) * step + min;

        // ensure value is in min/max range
        return clamp(newVal, min, max);
    });
}

I'm using lodash clamp for brevity here because its already in my project but you can just as easily run a manual check and set the value accordingly. In the scenario of passing this as props defaultValues={[41]} step={10} min={3} max={53} my handle would render at 43 as it should. This gets you almost all the way to success, but the internals of why step is not respected vs min/max is a mystery to me.

Infinity loop with `step` equals zero

There is a case that leads to the infinity loop and page freezing: passing step: 0.
The this.numOfMarks becomes Infinity and the loop on line 61 will go forever.

super(props);
this.numOfMarks = (props.max - props.min) / this.props.step;
this.schdOnMouseMove = schd(this.onMouseMove);
this.schdOnTouchMove = schd(this.onTouchMove);
this.schdOnEnd = schd(this.onEnd);
this.schdOnResize = schd(this.onResize);
this.thumbRefs = props.values.map(() => React.createRef<HTMLElement>());
for (let i = 0; i < this.numOfMarks + 1; i++) {
this.markRefs[i] = React.createRef<HTMLElement>();
}

What do you think about resetting step to the default value 1 in case of passed zero?

Specify algorithm or a hard coded list of slider values

AirBnB's rheostat, allows specifying an algorithm (deafult is linear, but others include log10 or geometric). This is useful if you have a wide range of values (e.g. 1 to 1,000), but the small values are much more significant. At the small end, each value can be adjusted one by one, at the large end, it follows the algorithm and adjusts in larger increments.

Alternatively, a hard coded list of values would be a good option too:
For example:

  • 1-99, use steps of 1
  • 100-195, use steps of 5
  • 200+, use steps of 10
  • 500+, use steps of 100

Is this possible in some way already? I may have missed it. Nice job building this slider!

getTrackBackground() not being triggered when there are two tracks

In our StyledTrack component, we used getTrackBackground() and passed the background color as a prop:

      <StyledTrack
        {...trackProps}
        values={trackPositionArr}
        isDragged={isDragged}
        $stylePreset={trackPresets.$sliderTrackStylePreset}
        $trackSize="sizing020"
        $thumbSize="sizing040"
        style={{
          background: getTrackBackground({
            values,
            colors,
            min: 0,
            max: duration,
          }),
        }}
        data-testid="audio-slider-track"
      >
        {trackChildren}
      </StyledTrack>

This works perfectly when there is only one track playing. However, when playing two tracks at the same time, getTrackBackground() in the second track didn't render the colour. Could you please advise how we could fix this?

Those two components are on the same page in Storybook:
Screenshot 2020-03-13 at 10 16 24
Screenshot 2020-03-13 at 10 16 32
Screenshot 2020-03-12 at 09 59 06

Warning emitted by isStepDivisible due to floating point value mismatch

I just noticed that if you try and create a Range control which has the following props <Range step={0.1} min={0.1} max={50} ... /> it will emit a The difference of max and min must be divisible by step warning.

This appears to be because (50 - 0.1) / 0.1 equals 498.99999999999994 in JavaScript, but the expression parseInt(res.toString(), 10) actually just returns 498. Most calculators would return 499 for both.

Thank you!

Custom Interactions to Dragable Track

Nice feature btw,

I would like to be able to set the draggable track to only work in between the two thumbs. Reason being is that my rendered thumbs are quite small so sometimes i click on the thumb part but the draggable track is then activated.

Injecting a new value to slider...

Hello, we have an application that reads all the light settings in our home, using DALI technology with RPi_HAT that can query each "ballast" that dims up/down from manual switches.

I've been trying to inject these light levels into my sliders, but can't find a way to do this once the slider has started.

Is there a simple way to inject a new value and have the slider move to that spot ?

Thank you !

Doesn't work in IE11 -- use of unsupported array methods

The docs state that it should work in IE11 but it doesn't currently because it uses these unsupported methods:
Array.from
Array​.find​Index
Math.sign

Would you be willing to swap those out, replace them with things that do work in IE11?

Looks like it should work other than those!

Thanks,
Jesse

Context:

Array.from

_this.getThumbs = function () {
            if (_this.trackRef && _this.trackRef.current) {
                return Array.from(_this.trackRef.current.children);
            }
            console.warn('No thumbs found in the track container. Did you forget to pass & spread the `props` param in renderTrack?');
            return [];
};

Array.findIndex

        _this.getTargetIndex = function (e) {
            return _this.getThumbs().findIndex(function (child) { return child === e.target || child.contains(e.currentTarget); });
        };

Math.sign -- in utils.js

    var res = Math.abs(remainder / BIG_NUM) < step / 2
        ? rounded
        : rounded + step * Math.sign(value);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.