Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Material-UI Slider - Changing Values using Scale

I want to display a range on the slider from 1000 to 1M and add the following markers

const followersMarks = [
    {
      value: 1000,
      label: '1k',
    },
    {
      value: 5000,
      label: '5k',
    },
    {
      value: 10000,
      label: '10k',
    },
    {
      value: 25000,
      label: '25k',
    },
    {
      value: 50000,
      label: '50k',
    },
    {
      value: 100000,
      label: '100k',
    },
    {
      value: 250000,
      label: '250k',
    },
    {
      value: 500000,
      label: '500k',
    },
    {
      value: 1000000,
      label: '1M',
    },
  ];

Am adding it as follows

<Form.Group controlId="formGridState">
              <Form.Label className="mb-15">Followers Range</Form.Label>
              <Slider
                value={followersRange}
                onChange={handleChangeFollowersRange}
                valueLabelDisplay="auto"
                aria-labelledby="range-slider"
                step="1000"
                valueLabelDisplay="on"
                marks={followersMarks}
                min={1000}
                max={1000000}
              />
            </Form.Group>

This is the result:

enter image description here

What do I need?

Since at the start of the range I am displaying more markers the legibility and UX are bad. Is there a way to use scale to show the range in such way that it takes 50-60% of the space to show the first 25% of the values and then space out the rest?

like image 680
Harsha M V Avatar asked May 14 '20 08:05

Harsha M V


1 Answers

Below is a working example of one way to do this. The key thing to note is that the values for min, max, step, and value (including value within marks) are linear values. The scale function then translates these to the non-linear values you want to display.

import React from "react";
import Typography from "@material-ui/core/Typography";
import Slider from "@material-ui/core/Slider";

const followersMarks = [
  {
    value: 0,
    scaledValue: 1000,
    label: "1k"
  },
  {
    value: 25,
    scaledValue: 5000,
    label: "5k"
  },
  {
    value: 50,
    scaledValue: 10000,
    label: "10k"
  },
  {
    value: 75,
    scaledValue: 25000,
    label: "25k"
  },
  {
    value: 100,
    scaledValue: 50000,
    label: "50k"
  },
  {
    value: 125,
    scaledValue: 100000,
    label: "100k"
  },
  {
    value: 150,
    scaledValue: 250000,
    label: "250k"
  },
  {
    value: 175,
    scaledValue: 500000,
    label: "500k"
  },
  {
    value: 200,
    scaledValue: 1000000,
    label: "1M"
  }
];

const scale = value => {
  const previousMarkIndex = Math.floor(value / 25);
  const previousMark = followersMarks[previousMarkIndex];
  const remainder = value % 25;
  if (remainder === 0) {
    return previousMark.scaledValue;
  }
  const nextMark = followersMarks[previousMarkIndex + 1];
  const increment = (nextMark.scaledValue - previousMark.scaledValue) / 25;
  return remainder * increment + previousMark.scaledValue;
};

function numFormatter(num) {
  if (num > 999 && num < 1000000) {
    return (num / 1000).toFixed(0) + "K"; // convert to K for number from > 1000 < 1 million
  } else if (num >= 1000000) {
    return (num / 1000000).toFixed(0) + "M"; // convert to M for number from > 1 million
  } else if (num < 900) {
    return num; // if value < 1000, nothing to do
  }
}

export default function NonLinearSlider() {
  const [value, setValue] = React.useState(1);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <div>
      <Typography id="non-linear-slider" gutterBottom>
        Followers
      </Typography>
      <Slider
        style={{ maxWidth: 500 }}
        value={value}
        min={0}
        step={1}
        max={200}
        valueLabelFormat={numFormatter}
        marks={followersMarks}
        scale={scale}
        onChange={handleChange}
        valueLabelDisplay="auto"
        aria-labelledby="non-linear-slider"
      />
      <Typography>Value: {scale(value)}</Typography>
    </div>
  );
}

Edit Slider scale


Here is a similar example, but modified for a range slider:

import React from "react";
import Typography from "@material-ui/core/Typography";
import Slider from "@material-ui/core/Slider";

const followersMarks = [
  {
    value: 0,
    scaledValue: 1000,
    label: "1k"
  },
  {
    value: 25,
    scaledValue: 5000,
    label: "5k"
  },
  {
    value: 50,
    scaledValue: 10000,
    label: "10k"
  },
  {
    value: 75,
    scaledValue: 25000,
    label: "25k"
  },
  {
    value: 100,
    scaledValue: 50000,
    label: "50k"
  },
  {
    value: 125,
    scaledValue: 100000,
    label: "100k"
  },
  {
    value: 150,
    scaledValue: 250000,
    label: "250k"
  },
  {
    value: 175,
    scaledValue: 500000,
    label: "500k"
  },
  {
    value: 200,
    scaledValue: 1000000,
    label: "1M"
  }
];

const scaleValues = (valueArray) => {
  return [scale(valueArray[0]), scale(valueArray[1])];
};
const scale = (value) => {
  if (value === undefined) {
    return undefined;
  }
  const previousMarkIndex = Math.floor(value / 25);
  const previousMark = followersMarks[previousMarkIndex];
  const remainder = value % 25;
  if (remainder === 0) {
    return previousMark.scaledValue;
  }
  const nextMark = followersMarks[previousMarkIndex + 1];
  const increment = (nextMark.scaledValue - previousMark.scaledValue) / 25;
  return remainder * increment + previousMark.scaledValue;
};

function numFormatter(num) {
  if (num > 999 && num < 1000000) {
    return (num / 1000).toFixed(0) + "K"; // convert to K for number from > 1000 < 1 million
  } else if (num >= 1000000) {
    return (num / 1000000).toFixed(0) + "M"; // convert to M for number from > 1 million
  } else if (num < 900) {
    return num; // if value < 1000, nothing to do
  }
}

export default function NonLinearSlider() {
  const [value, setValue] = React.useState([1, 25]);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <div>
      <Typography id="non-linear-slider" gutterBottom>
        Followers
      </Typography>
      <Slider
        style={{ maxWidth: 500 }}
        value={value}
        min={0}
        step={1}
        max={200}
        valueLabelFormat={numFormatter}
        marks={followersMarks}
        scale={scaleValues}
        onChange={handleChange}
        valueLabelDisplay="auto"
        aria-labelledby="non-linear-slider"
      />
      <Typography>Values: {JSON.stringify(scaleValues(value))}</Typography>
    </div>
  );
}

Edit Slider scale

like image 160
Ryan Cogswell Avatar answered Oct 12 '22 14:10

Ryan Cogswell