Giter Site home page Giter Site logo

sghall / react-compound-slider Goto Github PK

View Code? Open in Web Editor NEW
620.0 7.0 81.0 10.06 MB

:black_medium_small_square: React Compound Slider | A small React slider with no opinion on markup or styles

Home Page: https://react-compound-slider.netlify.com

License: MIT License

JavaScript 3.13% Shell 0.05% TypeScript 96.83%
react slider slider-range slider-component react-component multi-slider typescript javascript

react-compound-slider's Introduction

React Compound Slider

Welcome to the future. React Compound Slider is a small slider component with no opinion about markup or styles.

Coverage Status license npm downloads npm version Language grade: JavaScript

2020 Roadmap

Basic Overview:

  • Convert slider source to Typescript (done)
  • Convert tests to Typescript (done)
  • Convert demos and docs to Typescript (done)
  • Use hooks internally and export them
  • Drive state changes with a reducer that can be changed by the user

The upcoming versions of this slider will allow for more customization. I don't expect any significant changes for existing users.

Motivation

This library aims to be a stable platform for creating slider components with a very small impact on bundle size. It is primarily aimed at application developers and npm package maintainers. You can create your own set of controls matched exactly to your application style, but it takes a little more effort than other components out there. You need to be comfortable handling what gets rendered and styling your components to really get maximum value from this library. There are quite a few demos on the website but they should be used as a starting point. You can also create your own custom themed slider component for your favorite framework and release it on npm for others to use.

Slider Features

  • Small size
  • Makes no assumptions about your markup
  • Supports SVG sliders
  • Supports typescript
  • Precise control over user interactions and styling
  • Horizontal/vertical display
  • The display of values can be reversed
  • Supports mouse and touch events (tested in IE9+, Chrome, Firefox & Safari)
  • Supports keyboard events so handles can be moved using arrow keys
  • Create any type of slider (value, range, n-handled sliders)
  • Generates uniformly spaced, human-readable tick values to label your slider
  • Integrates seemlessly with any app styling approach (CSS, CSS-in-JS, Inline-styles)
  • Interaction modes (Allow crossing, Prevent crossing, Pushable mode, Create your own mode)
  • Works as a controlled component

More Examples on CodeSandbox

Installation

React Compound Slider is available as an npm package.

To install and save in your package.json dependencies, run:

// React 16.3 or greater
npm install react-compound-slider

// React 15.0 -> 16.2
npm install [email protected]

Documentation

The documentation is divided into several sections:

Example Usage

You have full control of everything that is rendered. Just render the Slider children that you want. Don't need ticks? Don't use Ticks. Don't want a rail? Don't use Rail. Just create the local rail, handle, track and tick components that you want and render those using whatever styling method you prefer.

You can use these components from the demos to jumpstart your slider:

Starter Components - Horizontal

Starter Components - Vertical

Starter Components - Material UI

Starter Components - With Tooltips

Basic Tooltip CSS (CSS from this demo)

import { Slider, Handles, Tracks } from 'react-compound-slider'
import { Handle, Track, Tick } from './your-local-slider-components'

  <Slider
    rootStyle={sliderStyle}
    domain={[0, 100]} // [min, max]
    values={[20, 60, 80]} // slider values
  >
    <Rail>
      {({ getRailProps }) => (
        <div style={railStyle} {...getRailProps()} /> // render your clickable rail!
      )}
    </Rail>
    <Handles>
      {({ handles, getHandleProps }) => (
        // render your handles!
      )}
    </Handles>
    <Tracks left={false} right={false}>
      {({ tracks, getTrackProps }) => (
        // render your (optional) tracks!
      )}
    </Tracks>
    <Ticks count={10}>
      {({ ticks }) => (
        // render your (optional) ticks!
        // count prop = auto generate approximately 10 uniformly spaced, human-readable ticks
      )}
    </Ticks>
  </Slider>

Approach

This library takes a compound component approach to creating sliders that separates the data/logic from presentation.

If you're familiar with Kent Dodd's work on Paypal's downshift or react-toggled then the pattern should seem familiar. The components use the function as child components pattern.

In practical terms this means you can create just about any kind of slider you can imagine and use whatever style approach you want. By taking this approach it also frees you up to render whatever markup you want to customize your slider. The Slider streams you the data and really only cares about the dimensions of the outer div where it takes its measurements from.

In general slider components are composed of a relatively positioned outer div with elements absolutely positioned inside by a percentage. In this library the Handles, Tracks, and Ticks components are used as children to the Slider component and they let you tap into a stream of values and percentages that you can then use to render your own components.

Slider Artwork by Guilhem from the Noun Project

react-compound-slider's People

Contributors

amaidah avatar armata007 avatar bvandenbos avatar cameracker avatar devin2712 avatar dlwalsh avatar guy-kdm avatar josepot avatar larsonjj avatar m7kvqbe1 avatar msftenhanceprovenance avatar reicheltp avatar sghall avatar tbassetto avatar xinsight 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  avatar  avatar

react-compound-slider's Issues

Crossing handles while using mode 1 and onUpdate creates push effect

Problem or feature description:

When a slider is configured to use mode 1, uses onUpdate for the change hook, and is controlled, crossing the handles creates a push effect.

https://codesandbox.io/s/kokrqqxwor

Steps to reproduce (for problems):

  1. Define a slider with more than one handle
  2. Specify mode 1
  3. Use onUpdate event hook
  4. Use a controlled slider
  5. Cross handles
  6. Observe that both handles are pushed

Versions (for problems):

React-Compound-Slider: 0.15.1

React: 16.x

Browser: Chrome

Operating System: Linux

MouseDown then Drag has non-standard behavior

This is a minor style issue, but one in which it seems react-compound-slider is somewhat non-standard.

If you mouseDown on <Track> or <Rail>, the <Head> immediately jumps to that position. This is good. However, if you then immediately try to drag the <Head> it won't work - you need to mouseUp and then mouseDown directly over the <Head> a second time for dragging to be enabled.

There are certainly other sliders out there that exhibit this same behavior (eg react-rangeslider). However, the HTML5 native <input> slider does not, and nor does rc-slider or the sliders in youtube, wistia, plyr.io, etc. For these, if you mouseDown you can immediately start dragging without a second click. It seems that perhaps react-compound-slider is in the less standard group with respect to this behavior, and isn't as <input>-consistent as it could be. Was there a reason that it was implemented this way, or might it be worth changing?

Tooltip examples do not work if the browser window is resized

Problem or feature description:

If you resize the browser window of any of the react-compound-slider examples with tooltips (over the rail/tracks), the value is not correct.

Steps to reproduce (for problems):

Run the sample https://codesandbox.io/s/pjwwzzj8qm. Resize the browser window (without selecting a handle in the slider). Then hover over the rail/tracks, and notice the value is not correct. If you then click and move one of the handles, it now fixes the problem. The hover tooltip over the rail/tracks is now correct. Very odd.

Versions (for problems):

React-Compound-Slider:
0.21.1

React:
16.8.2

Browser:
Chrome

Operating System:
Windows 8.1 or 10

Alternating Track Display

New feature description:

When I am looking to adjust four (or any even number of) handles on my slider, I would think a common use case would be that the track displays would alternate.

In the case of 4 handles, the track between handles 1 and 2 would be "active" (i.e. use track color), the track between handles 3 and 4 would be "active", but the track between handles 2 and 3 would be "inactive" (i.e. use rail color), along with left and right being "inactive" as can presently be set.

Therefore, I am requesting, for a feature, the ability to alternate "active" tracks when the number of handles is even.

Visual Example

This is what it looks like today:

slider_request

This is what it would look like with the feature added:

slider_request_2

Distinguish user value changes from programatic ones

Problem or feature description:

First off, thanks for this nice module, it's really awesome.

I'm having an issue where it's hard to track wether the user changed the value, or it changed from the outside (via props). Think of a use case where you want to use the slider to act as a video seek bar. The handle should track the video current time (from outside prop), but the user should be able to modify it. While the user is modifying it, the handle should not track the outside prop and respect the user's input values (onChange, onUpdate).

I was trying to use onSlideStart/End to set a state isSeeking and not track the outside prop while this state is true, but it's not consistent with changing the value by clicking on the rail.

One idea to solve this could be to also include clicking on the rail (and thus sliding) in the onSlideStart and onSlideEnd callbacks too, but one would loose exclusivity for drag events.

Another approach would be to introduce some new callbacks that includes all user events. Something like onInputStart(event, value) onInputEnd(event, value).

What do you think? I could have a stab at a PR, but the API should be decided first.

Showcase

Try clicking on the rail to reposition handle vs dragging the handle.

https://codesandbox.io/s/y3mo40wqv1

Slider no longer accepts `[undefined]`

Problem or feature description:

Something has changed since 0.20.1 where previously react-compound-slider accepted [undefined] and now it does not. i.e. where you have one handle but have not passed a value into the component. Seems to provide NaN as the value when this occurs.

I'm not actually sure this is even an issue, as it does kind of make sense, but posting it here in case anyone else has the same problem.

I was able to work around this by changing my code to be

<Slider values={value ? [value] : [min]}>

Which ensure a value is always provided (since we default min to 0).

This also works, but then renders no handles until a value is supplied..

<Slider values={value ? [value] : []}>

Steps to reproduce (for problems):

Versions (for problems):

React-Compound-Slider: 1.10

React: 16.0.0

Browser: Chrome 72.0.3626.119

Operating System: OSX Mojave 10.14.1

Add keyboard navigation

Problem or feature description:

From an accessibility point of view it would be great to let the user use the keyboard to move the handles once they have the focus. It's unclear to me yet if a change needs to happen in react-compound-slider. If not, if it can be handled only in the Handle component we use, we should make sure that the examples are accessible.

Steps to reproduce (for problems):

Open https://sghall.github.io/react-compound-slider/#/slider-demos/horizontal and try to use the keyboard to move the handles. It doesn't work.

Versions (for problems):

React-Compound-Slider: 0.15.1

React: 16.3.2

Browser: Firefox Nightly

Operating System: macOS 10.13.6

Component Documentation Incorrect

Problem or feature description:

I am currently attempting to use RCS (with TypeScript) to build a height slider (for people) where the tracks represent half-inch increments and the labels represent "landmarks" like "5 ft." or "5 ft. 6" or "6 ft." The problem is I keep finding the documentation to be outdated on major components. Thankfully I found this example by @sghall, but it's missing the Ticks component. I figured that out, but now I am having issues with Handle and Tick.

Can this example be supplemented with more components so I (and imagine others) can have something to work from?

Thanks.

FWIW here is the component:

<Slider
                mode={1}
                step={1}
                domain={}
                rootStyle={sliderStyle}
                onChange={this.onChange}
                values={values}
            >
                <Rail>
                    {({ getRailProps }) => (
                        <div style={railStyle} {...getRailProps()} />
                    )}
                </Rail>
                <Handles>
                    {({ handles, getHandleProps }) => (
                        <div className="slider-handles">
                            {handles.map(handle => (
                                
                                <Handle
                                    key={handle.id}
                                    handle={handle}
                                    domain={handle.id}
                                    getHandleProps={getHandleProps}
                                />
                            ))}
                        </div>
                    )}
                </Handles>
                <Tracks right={false}>
                    {({ tracks, getTrackProps }) => (
                        <div className="slider-tracks">
                            {tracks.map(({ id, source, target }) => (
                                <Track
                                    key={id}
                                    source={source}
                                    target={target}
                                    getTrackProps={getTrackProps}
                                />
                            ))}
                        </div>
                    )}
                </Tracks>
                <Ticks count={5}>
                    {({ ticks }) => (
                        <div className="slider-ticks">
                            {ticks.map(tick => (
                                <Tick key={tick.id} tick={tick} count={ticks.length} />
                            ))}
                        </div>
                    )}
                </Ticks>
</Slider>

Steps to reproduce (for problems): N/A

Versions (for problems): 0.15.0

React-Compound-Slider: 0.15.0

React: 16.4.10 (with TypeScript)

Browser: Chrome Version 69.0.3497.81 (Official Build) (64-bit)

Operating System: macOS High Sierra 10.13.6

Add the possibility to customize the scale

Feature requests

Probably out of scope for this package but I will ask anyway: I would like to use the slider to adjust the frequency for an audio device. The thing is the possible values goes from 20 to 20000 and my first issue is that I guess the following warning in the console:

Warning: react-compound-slider: Increase step value. Found 19,981 values in range.

Is it really an issue to have that many steps? Can I silence the warning?

Also, in theory configuring the frequency works best with a logarithmic scale. Do you think that customizing the scale could be a feature added to this component?

Problem with handles in tutorial

Problem or feature description:

I am following the tutorial. At the second step where we add handles, <Slider.Handles> children function receives an object whose handles property is undefined. Therefore, calling handles.map throws an error.

Steps to reproduce (for problems):

<Slider
    rootStyle={sliderStyle}
    domain={[0, 100]}
    step={1}
    mode={2}
    values={[30]}
  >
    <div style={railStyle} />
    <Slider.Handles>
      {({ handles, getHandleProps }) => (
// handles is undefined here
        <div className="slider-handles">
          {handles.map(handle => (
            <Handle
              key={handle.id}
              handle={handle}
              getHandleProps={getHandleProps}
            />
          ))}
        </div>
      )}
    </Slider.Handles>
  </Slider>

Versions (for problems):

React-Compound-Slider:
^0.11.0

React:
^16.3.2

Browser:
Chrome 66.0.3359.170

Operating System:
Windows 10 (1709)

Add support for undefined values

Problem or feature description:

I've been trying to support the case where there is no initial value for the slider, and it's surprisingly tricky. (For example, I need the UI to differentiate between the user selecting zero and the slider defaulting to zero.)

I've managed to add this with an 'isDirty' flag in the <Slider> component props:

onSlideStart: () => { isDirty = true }

And then i can pass isDirty as a prop to the <Handle>.

However, it would be much nicer to be able to set the slider's value to undefined.

Passing an undefined value to the Slider results in a warning and defaults to the max value, so any caller to Slider would currently do the following like this:

values: [defaultValue || 0]

Are you open to the idea of allowing undefined values?

String Values for Ticks

Problem or feature description:

This is a question. I have been trying to create a simple two-handle slider with string values rather than integers. I have tried creating something from a fork of the example to represent a range of sizes. I would like the ticks to be "XS," "S," "M," "L," "XL." I presume I need a mapping of these string keys to numerical values (e.g. 0-4) so the slider can do some math, but I haven't been able to get this to work. Is this even possible?

Steps to reproduce (for problems): N/A

Versions (for problems):

React-Compound-Slider: 0.15.0

React: 16.4.10 (with TypeScript)

Browser: Chrome Version 69.0.3497.81 (Official Build) (64-bit)

Operating System: macOS High Sierra 10.13.6

Inline SVG support

Hey there! I'm trying to figure out how to use this library with an inline SVG, with each library component rendering separately onto a single root <svg />. I think this should be possible, due to the nominally unopinionated nature of compound components, but there seems to be a problem. The issue is two-fold:

  1. <Slider /> wraps its children in (i.e. it renders as) a <div />. This means that it can't be within the root <svg /> element.
  2. <Slider /> expects its immediate children to be <Handles />, <Ticks />, etc. This means that that the root <svg /> element can't be within it either! (Unless each component gets a separate <svg />, which isn't especially desirable.)

It doesn't seem like you can make either way work. Is there anything I'm missing that pokes a hole in that reasoning?

As it stands now, I think that <div /> might be the single opinion the library does have about rendering. Have you considered adding an optional prop to <Slider /> that determines the root element? This is similar to @material-ui/core's <Typography /> and its component prop. React.Fragment might be a nice option as well, since it is truly abstract. Then you could do something like this:

<svg
  xmlns={SVG_XMLNS}
  {...{ width, height }}
  viewBox={`0, 0, ${width}, ${height}`}
>
  <Slider
    component={React.Fragment}
    {...otherProps}
  >
    <Ticks>
      {({ ticks }) => (
        <>
          {ticks.map(({ id, percent }) => (
            <g
              className="slider-tick"
              transform={`translate(${percent * width}, ${height / 2})`}
              key={id}
            >
              <path d={/* ... */ } />
            </g>
          ))}
        </>
      )}
    </Ticks>
  </Slider>
</svg>

Adding tooltip?

Great library. Thanks.
Is there a way to add a tooltip to the handle, as several sliders provide? I've tried using react-tooltip-lite to wrap the Handle, but that seems to give a tooltip that centers on the rail and never moves.

Very slow with delay in Real Mobile Phone

Problem or feature description:

Problem: when use this component in real mobile phone, with two HandleComponent to make a range value like ArBNB, it has a big delay in change value.

How we can resolve this in real mobile phone?

Steps to reproduce (for problems):

Run this components in Real Mobile Phone.

Copy and past in browser:
http://www.giphy.com/gifs/iel2jCJrsLGBQa8J0P

In this gif I show in browser desktop simulated a phone, in this case we do not have problem. Only in real phone.

Versions (for problems):

React-Compound-Slider:

Component.js

// @flow weak

import React from "react";
import PropTypes from "prop-types";
import Typography from "@material-ui/core/Typography";
import { withStyles } from "@material-ui/core/styles";

// *******************************************************
// RAIL COMPONENT - Barra relative - fica por baixo
// *******************************************************

const railStyle = theme => ({
  root: {
    position: "absolute",
    width: "100%",
    height: 10,
    borderRadius: 7,
    cursor: "pointer",
    backgroundColor: theme.palette.grey[200]
  }
});

function RailComponent({ classes, getRailProps }) {
  return <div className={classes.root} {...getRailProps()} />;
}

RailComponent.propTypes = {
  classes: PropTypes.object.isRequired,
  getRailProps: PropTypes.func.isRequired
};

export const Rail = withStyles(railStyle)(RailComponent);

// *******************************************************
// HANDLE COMPONENT - Bolinhas
// *******************************************************

const handleStyle = theme => ({
  root: {
    position: "absolute",
    marginLeft: "-11px",
    marginTop: "-6px",
    zIndex: 2,
    width: 24,
    height: 24,
    cursor: "pointer",
    borderRadius: "50%",
    boxShadow: "1px 1px 1px 1px rgba(0, 0, 0, 0.2)",
    border: theme.palette.primary.contrastText,
    backgroundColor: "#f15a22"
  }
});

function HandleComponent({
  divOrButton: Comp,
  domain: [min, max],
  handle: { id, value, percent },
  classes,
  getHandleProps
}) {
  return (
    <Comp
      role="slider"
      aria-valuemin={min}
      aria-valuemax={max}
      aria-valuenow={value}
      className={classes.root}
      style={{ left: `${percent}%` }}
      {...getHandleProps(id)}
    />
  );
}

HandleComponent.propTypes = {
  divOrButton: PropTypes.oneOf(["div", "button"]).isRequired,
  domain: PropTypes.array.isRequired,
  handle: PropTypes.shape({
    id: PropTypes.string.isRequired,
    value: PropTypes.number.isRequired,
    percent: PropTypes.number.isRequired
  }).isRequired,
  classes: PropTypes.object.isRequired,
  getHandleProps: PropTypes.func.isRequired
};

HandleComponent.defaultProps = {
  divOrButton: "div"
};

export const Handle = withStyles(handleStyle)(HandleComponent);

// *******************************************************
// TRACK COMPONENT -- BARRA Absolute
// *******************************************************

const trackStyle = theme => ({
  root: {
    position: "absolute",
    height: 10,
    zIndex: 1,
    borderRadius: 7,
    cursor: "pointer",
    backgroundColor: "#f15a22"
  }
});

function TrackComponent({ classes, source, target, getTrackProps }) {
  return (
    <div
      className={classes.root}
      style={{
        left: `${source.percent}%`,
        width: `${target.percent - source.percent}%`
      }}
      {...getTrackProps()}
    />
  );
}

TrackComponent.propTypes = {
  source: PropTypes.shape({
    id: PropTypes.string.isRequired,
    value: PropTypes.number.isRequired,
    percent: PropTypes.number.isRequired
  }).isRequired,
  target: PropTypes.shape({
    id: PropTypes.string.isRequired,
    value: PropTypes.number.isRequired,
    percent: PropTypes.number.isRequired
  }).isRequired,
  classes: PropTypes.object.isRequired,
  getTrackProps: PropTypes.func.isRequired
};

export const Track = withStyles(trackStyle)(TrackComponent);

// *******************************************************
// TICK COMPONENT
// *******************************************************

const tickStyle = theme => ({
  tick: {
    position: "absolute",
    marginTop: 14,
    width: 1,
    height: 5,
    backgroundColor: theme.palette.text.primary
  },
  label: {
    position: "absolute",
    marginTop: 22,
    textAlign: "center"
  }
});

export function TickComponent({ classes, tick, count, format }) {
  return (
    <div>
      <div className={classes.tick} style={{ left: `${tick.percent}%` }} />
      <Typography
        className={classes.label}
        style={{
          marginLeft: `${-(100 / count) / 2}%`,
          width: `${100 / count}%`,
          left: `${tick.percent}%`
        }}
      >
        {format(tick.value)}
      </Typography>
    </div>
  );
}

TickComponent.propTypes = {
  tick: PropTypes.shape({
    id: PropTypes.string.isRequired,
    value: PropTypes.number.isRequired,
    percent: PropTypes.number.isRequired
  }).isRequired,
  classes: PropTypes.object.isRequired,
  count: PropTypes.number.isRequired,
  format: PropTypes.func.isRequired
};

TickComponent.defaultProps = {
  format: d => d
};

export const Tick = withStyles(tickStyle)(TickComponent);

InputRange.js

import React, { Component } from "react";
import PropTypes from "prop-types";
import Slider, { Handles, Tracks, Ticks } from "react-compound-slider";
import { withStyles } from "@material-ui/core/styles";
import { Rail, Handle, Track, Tick } from "./Component";

const style = () => ({
  root: {
    height: 120,
    width: "90%",
    margin: "0 auto"
  },
  slider: {
    position: "relative",
    width: "100%"
  }
});

const domain = [100, 500];
const defaultValues = [150, 450];

class InputRangeMT extends Component {
  state = {
    values: defaultValues.slice(),
    update: defaultValues.slice()
  };

  onUpdate = update => {
    this.setState({ update });
  };

  onChange = values => {
    console.log("VALUES", values);
    this.setState({ values });
  };

  render() {
    const {
      props: { classes },
      state: { values, update }
    } = this;

    return (
      <div className={classes.root}>
        <Slider
          mode={2}
          step={10}
          domain={domain}
          className={classes.slider}
          onUpdate={this.onUpdate}
          onChange={this.onChange}
          values={values}
        >
          {/* <div>E ai Manoel</div> */}
          <Slider.Rail>
            {({ getRailProps }) => <Rail getRailProps={getRailProps} />}
          </Slider.Rail>
          <Handles>
            {({ handles, getHandleProps }) => (
              <div>
                {handles.map(handle => (
                  <Handle
                    key={handle.id}
                    handle={handle}
                    domain={domain}
                    getHandleProps={getHandleProps}
                  />
                ))}
              </div>
            )}
          </Handles>
          <Tracks left={false} right={false}>
            {({ tracks, getTrackProps }) => (
              <div>
                {tracks.map(({ id, source, target }) => (
                  <Track
                    key={id}
                    source={source}
                    target={target}
                    getTrackProps={getTrackProps}
                  />
                ))}
              </div>
            )}
          </Tracks>
          {/* <Ticks count={2}>
            {({ ticks }) => (
              <div>
                {ticks.map(tick => (
                  <Tick key={tick.id} tick={tick} count={ticks.length} />
                ))}
              </div>
            )}
          </Ticks> */}
        </Slider>
      </div>
    );
  }
}

InputRangeMT.propTypes = {
  classes: PropTypes.object.isRequired
};

export default withStyles(style)(InputRangeMT);

React: 16

Browser: Moto Z2 - Chorme

Operating System: Android 7.1

Inline Styles with Strict Content Security Policy

Problem or feature description:

This is just a heads-up that because the current implementation uses inline styles it violates a strict Content Security Policy (CSP) one might use to prevent XSS attacks. Some might be OK relaxing the CSP by using unsafe-inline, but that basically defeats the purpose.

All is not lost though since you can use mechanisms like a Webpack nonce. Here is an example of the way styled-components uses it.

I suggest that the documentation notes that this could be an issue for some and suggests ways to address it like incorporating a Webpack nonce (for those who use Webpack) with react-compound-slider.

Steps to reproduce (for problems):

Versions (for problems):

React-Compound-Slider: 0.15.0

React: 16.4.10

Browser: N/A

Operating System: N/A

Follow and add value on Slider

Problem or feature description:

OnHover I would like to see the value itself and on click I want to add the actual value to the values array.

Not found a clear description in he readme for this. (Probably I missed it)

Thanks

Steps to reproduce (for problems):

Versions (for problems):

React-Compound-Slider:

React:

Browser:

Operating System:

API change: `values` vs `defaultValues`

Problem or feature description:

API change: values vs defaultValues

I'm working on a project where I need to be able to update a Slider with the values of a couple input components and vice-versa.

The problem with the fact that Slider is a stateful component is that once I've set the default values, the only way that I have to externally "update" it is to use the key property to force a componentWillMount while I provide the new values through defalutValues... Which is quite hacky and not great performance-wise.

I think that a slightly improved version of this API would be to have a values prop instead of a defaultValues prop. The idea would be that only when the property values changes the component gets updated with the "external" values. Meaning that the component would still be stateful, but if the user provides a new Array of values, then those new values would take preference over its internal state.

There is only one tricky situation: what would happen if the domain, step or reversed properties get updated?

It depends on whether the values property also gets updated:

  • If the values prop hasn't changed: then the component will re-calculate the new values based on the new range and then it would call the onUpdate and onChange functions (same behaviour that we have now).

  • If the values prop has also changed: then those values will take preference and there is no point on calling the onUpdate and onChange functions.

Another advantage of this approach is that it's easy to make it backwards compatible with the current version.

Disable handles dynamically

Problem or feature description:

There are cases where you would like to disable handles dynamically.
Couldn't find anything related to it in source code. Could that be added ?

Hard to grab narrow bar

Hi,

Nice work! I just have a usability question:

Problem or feature description:

I am sizing my bar (Rail/Track) based on the Material Design spec: https://material.io/design/components/sliders.html#spec

const trackHeight = 2;
const thumbHeight = 12;

However, this makes it tricky to click the bar. This is a low priority question as it still works and is usable. I just wanted to thicken up the "hot spot" for mouse interaction. I tried using margin styling but even though the style was applied (more space around the bar) the actual "hot spot" was the same. I tried height and margin on all components just to see if any are used but no luck.

Any ideas?

Reversed Slider

How would I reverse the start point of the handle from the left of the slider to the right, basically, it would act similarly to the reversed mode but also have the handle reversed along with the slider values.

Feature Request: Expose optional handle ID for onUpdate

Problem or feature description:

First of all, thank you for great slider. It works great and is very flexible unlike some of the other react sliders I've tried.

It would be great to know what handle is being dragged. I briefly looked at the tutorial and documentation but could not find it so apology if this already exists. I am passing custom handle IDs and would like to map the dragged handles values based on those IDs.

My use case is I have multiple (> 2) handles that can cross each other and need to track which value is for which parameter. Position would not be enough for me to map the values back to the right attributes. Any alternative thoughts with react-compound-slider that exposing handle ID would be appreciated too.

Changing Steps In the Domain

Problem or feature description:

This is a question, and the matter at hand may very well be out-of-scope for the slider. But it never hurts to ask.

I have a range of values that, to use Scala syntax, looks like (30 to 34) ++ (36 to 42 by 2). In other words, 30-34 with step 1 followed by 36-42 with step 2. I am simply curious if the ability to "change steps" midway through the domain is even possible with the slider and how if so.

Steps to reproduce (for problems):

Versions (for problems):

React-Compound-Slider:

React:

Browser:

Operating System:

[Question] Range length check (not more than 1M values)

Problem or feature description:

Slider range is too large. Increase step value

I have a range slider with two handlers (using to filter the price)
I can't put the domain min 0, max 1M (or more) and step 1 because there is a check to not have values greater than 1M.
image

I don't know if this is a good check because in real world app the min/max values are dynamic configurable and it's possible to have more than 1M ranges.

My question is:
Can you explain way and how can I rid of that?

Versions:
React-Compound-Slider: 0.20.1
React: Latest
Browser: Chrome Latest
Operating System: Windows 10

Tooltips Typescript: activeHandleID is missing in HandlesObject interface

Problem or feature description:

activeHandleID is missing in types. In a typescript project, his makes it impossible to use Tooltips as described in docs without creating additional custom interfaces.

Doing

<Handles>
    {({ handles, getHandleProps, activeHandleID }) => (
    ...

throws

[ts] Type 'HandlesObject' has no property 'activeHandleID' and no string index signature.

Steps to reproduce (for problems):

Take a look at:
https://github.com/sghall/react-compound-slider/blob/master/src/Handles/Handles.d.ts

Versions (for problems):

React-Compound-Slider: 0.19.1

React: latest

Browser: Chrome 71.0.3578.98

Operating System: OSX 10.14.2

Updating steps dynamically

Is there a safe proper way to update steps on behalf?

Like:
step=0.1 if onUpdate value < 1
step=0.5 if onUpdate value < 2
step=1 if onUpdate value > 2

Tried to modify state of step onUpdate and pass it as step prop, but that doesn't seems to work well.

Any ideas?

Max Range

Is adding a max selected range on the road map?

I currently have a use case where I need to cap the selected range to x number of ticks. For example, having a range of 12 hours on the slider and having a max selection of 6 of those hours.

Thanks for making this great library! It's been a pleasure to work with ๐Ÿ˜„

Mouse events over <Track>

Many sliders, eg the ones on youtube or https://plyr.io/ provide a moving tooltip as you hover over the slider bar. I'm trying to reproduce this on react-compound-slider. But the rcs slider bar consists of (at least) two parts. The part to the right of the Handle is the Rail and I can capture its mouse events like this:

<Rail>
    {({ getRailProps }) => (
        <div onMouseEnter={this.mouseOverRail} onMouseMove={this.mouseOverRail} onMouseLeave={(e) => this.mouseOverRail(null)} className='rccrail' {...getRailProps()} />
    )}
</Rail>

But the part to the left of the rightmost Handle consist of some <Track>s, which are defined differently. I can't see how to add mouse events to these and have them handled by the same component that handles the mouseOverRail events.

Is there an example of how to do this? Thanks!

Changing Slider domain and value props at the same time creates error in setValues

Problem or feature description:

Changing the domain and value props of the slider to a domain that is smaller can cause an "invalid value" warning from the slider.

This is reproducible with the following codesandbox: https://codesandbox.io/s/5woq82rz6k

The root cause appears to be here: https://github.com/sghall/react-compound-slider/blob/master/src/Slider/Slider.js#L82

In this control flow, the conditional on line 68 evaluates to true which causes the range to be updated, and onChange and onUpdate to fire, but the code continues to call setValues on line 84 using a stale set of values. This results in the slider ignoring the value prop that is actually being set, and it uses the old value set which is often out of range. The value is then reset to an incorrect value.

Steps to reproduce (for problems):

  1. Mount a slider with a domain and value set.
  2. Change the domain and values at the same time to a domain smaller than the original props.
  3. Observe that an error is logged to the console stating the range was reset.

Versions (for problems):

React-Compound-Slider: 0.15.1

React: 16.

Browser: Chrome

Operating System: Linux

Incorrect initial slider dimensions for vertical sliders

Problem or feature description:

When rendering tooltips on a custom Rail component of a vertical Slider the tooltips are incorrectly aligned until the user clicks on the rail and the dimensions are correctly recalculated.

The bug is caused by Slider.componentDidMount reading vertical from this.state instead of this.props. Slider.js L171

The problem is fixed once the user clicks on the rail because then handleRailAndTrackClicks is called which updates the dimensions again, but this time reading vertical from props. See Slider.js L271

Steps to reproduce (for problems):

  1. Render a vertical slider from 0-100 with a custom Rails component that render tooltips when hovering over the rail.
  2. Hover over the rail to see the tooltip incorrectly aligned
  3. Click on the rail to set the value
  4. Hover over the rail again and the tooltips are now correctly aligned.

Versions (for problems):

React-Compound-Slider: 0.19.1

React: 16.7.0

Browser: Chromium Version 72.0.3626.81

Operating System: Arch Linux 4.20.6-arch1-1-ARCH

Unable to make component controlled

Problem or feature description:

I am unable to control the component.

I want to set the values and pass them to the component myself to determine which value should be set.

I send an updated values array when I update state, but its ignored by the component. I can slide it manually even if I don't update the value.

Steps to reproduce (for problems):

I used all your example code.

Versions (for problems):

Master branch on github

React-Compound-Slider:

React:

Browser:
Chrome 71

Operating System:
OSX

"values" props ignored during sliding

Problem or feature description:

componentWillReceiveProps ignores the incoming values prop if the slider is "active" (sliding). This prevents folks from building a multi-handle slider where one handle responses to another.

https://github.com/sghall/react-compound-slider/blob/master/src/Slider/Slider.js#L72

A couple possible solutions:

  1. Always update values when given new props. Slider would acted like a controlled component.
  2. Only update the non-active values.

Feature request: onSlideStart and onSlideEnd

It would be convenient to expose events for beginning and ending a slide operation (from mouseDown/touchStart to mouseUp/touchEnd) from the Slider component. This would give slider users more control over the user experience.

I believe one could stitch this together by passing around custom mouseDown/touchStart handlers to Handle and Rail components and inferring mouseUp from the onChange event (or binding it oneself on document), but it's a bit cumbersome.

If it makes sense to add this feature, I could maybe take a crack at a PR.

Demo page can't be used with wide windows

When the page is wide enough, the navigation bar stays open forever. But when the navigation bar is open, the content of the page is disabled. It can't be scrolled and it can't be interacted with. I had to reduce the window size to be able to play with the examples.

Feature Request: Provide update/change event when automatically rounding intial value to nearest step

First off, love the slider! Excellent work...

I noticed during some testing that when providing an initial value to the slider that is outside of the step boundaries, the slider will automatically round the value to the nearest valid step. This is fine! However, there is no update or change event raised for this change. For users that are using a state management system, the lack of an update/change event prevents us from properly updating the state for the slider.

Would love it we could receive the proper events for this initial change.

Steps to reproduce:

  • Build your slider
  • Set the step for your slider to 10
  • Set the initial value to 35
  • Set your callback for the onChange/onUpdate event
  • Add a breakpoint within the callback
  • Test it. The value for the slider will automatically be changed to 40 and no events will be raised

This was tested on both Chrome and Firefox browsers.

Hiccups in pushable header mode

In pushable mode (mode 3), you can use one handle to slowly push another one.
However, if you move your pointer too fast, both handles suddenly stop dead.
This is always a little irritating, but can be more puzzling if you're on a slider with constraints - when the handles suddenly stop, your first assumption is that you violated some separation constraint. To have to go back and perform the same action more gently is confusing.

"Continuum Mode" for video playback

I'm using react-compound-slider as the progress bar for video playback (ie just like on youtube etc it can be used to reposition video, or while the video is playing, it just shows the progress updating - very standard stuff.)

I'm running into a problem when the video is playing - I get the console message
Warning: react-compound-slider: Invalid value encountered. Changing 1822413.5420038146 to 1822005
ie, it's changing value to the closest multiple of step. This upsets the video player component which then stutters as it keeps having its time value reset by the rounding algorithm.

Is there a good way around this? I'd like some sort of "continuum mode", where the step size is effectively zero, so there's no rounding done.

Bug?: Drag threshold for triggering a move event on the slider is off center

Problem or feature description:

When dragging the handle of a slider, the handle doesn't seem to change positions at the mid point between ticks. It looks like the point at which it moves the handle is about 50% off the actual midpoint

Reproducible here: https://codesandbox.io/s/5woq82rz6k (the button can be ignored here, not relevant)

http://recordit.co/UrOJcxpQWf

In a similar vein, the clicking of the track behaves in the same way too. Clicking slightly to the left or right of the true midpoint does not correctly calculate the nearest tick to use as the destination of the new handle - instead the "midpoint" is about 50% off the mark.

Steps to reproduce (for problems):

  1. Have a slider with a step
  2. Drag the handle over to another tick
  3. Observe the handle position change doesn't happen at the mid point.

Versions (for problems):

React-Compound-Slider: 0.15.1

React: 16

Browser: Chrome

Operating System: Linux

Dynamical setup of slider component

Hello!
I have one issue. When I'm trying to pass props to my child component to dynamically setup such properties of Slider as domain or values I get error "value.toFixed is not a function". So it looks like Slider only works with static property values.
Is it possible to use react-compound-slider with props passed as property values?

[Question] Snapping to values

Hi, I'm using your library for a project of mine and I want to implement snapping to values in a list. For example, I have the following ticks: 10, 20, 24 and 100, I want the user to snap between then. I cannot define a common step value as my ticks could vary in difference. I hope this make sense.

Wrong type definition for mode function

Hi!
First of all thanks for this library, I really like its flexibility and customizability :)

Problem or feature description:

Slider.d.ts defines the Slider prop 'mode' as number | (() => ReadonlyArray<number>), but both arguments and return types are different.
A correct definition could be:

export type SliderModeValue = {
  key: string;
  value: number;
}
export type SliderModeFunction = (
  current: SliderModeValue[],
  next: SliderModeValue,
  step: number,
  reverse: boolean,
  getValue: (x: number) => number
) => ReadonlyArray<SliderModeValue>

export interface SliderProps {
  mode?: number | SliderModeFunction;
  ...
}

Steps to reproduce (for problems):

Passing a function with arguments to mode property raise a type error in Typescript

Versions (for problems):

0.16.2


If you like, I provided you with a pull request to fix this issue :)

Thanks,

Lorenzo

cpu load issue on big value for domain

Problem or feature description:

Cpu Load issue and tab crash when using big value in domain.
I want to use a Slider for time interval so i use unix timestamp in domain do to so.
when i change the value for smaller one (like 0 to 100) it work no problem

Steps to reproduce (for problems):

<Slider domain={[1388530800000, 1535752800000]}
    values={[ 1530752800000 ]}
    rootStyle={{
        position: 'relative',
        width: '100%',
        height: 80,
        border: '1px solid steelblue',
    }}>
    <div style={{
        position: 'absolute',
        width: '100%',
        height: 10,
        marginTop: 35,
        borderRadius: 5,
        backgroundColor: '#8B9CB6',
    }} />
</Slider>

Versions (for problems):

{
    "react-compound-slider": "^0.16.1",
    "react": "^16.3.2",
}

Browser: Chrome 69.0.3497.100

Operating System: Windows 10

Error: 'ticks' is not exported by node_modules/d3-array/dist/d3-array.js

Problem or feature description:

Hitting this error when building with react-compound-slider @ 0.15.1.

$ rollup -c

index.ts โ†’ dist/index.js...
[!] Error: 'ticks' is not exported by node_modules/d3-array/dist/d3-array.js
https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module
node_modules/react-compound-slider/es/Slider/LinearScale.js (3:9)
1: function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2:
3: import { ticks } from 'd3-array';
            ^
4:
5: var LinearScale = function () {
Error: 'ticks' is not exported by node_modules/d3-array/dist/d3-array.js

Steps to reproduce (for problems):

Using 0.15.1 (doesn't occur with 0.15.0), and the following rollup config:

import resolve from 'rollup-plugin-node-resolve';
import typescript from 'rollup-plugin-typescript2';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';

const pkg = require('./package.json');

export default {
  input: 'index.ts',

  output: [{ file: pkg.main, format: 'cjs' }],

  watch: { clearScreen: false },
  plugins: [
    peerDepsExternal({ includeDependencies: true }),
    commonJS(),
    typescript({
      exclude: ['storybook/**'],
    }),
    resolve(),
  ],
};

Versions (for problems):

React-Compound-Slider: 0.15.1

React: 16.5.1

Browser:

Operating System:

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.