Last active
December 4, 2019 01:01
-
-
Save cinderblock/6f52313a06ad3f8bf0e0290826d1ef10 to your computer and use it in GitHub Desktop.
Initial version of a Logarithmic wrapper for rc-slider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from 'react'; | |
import Slider, { SliderProps } from 'rc-slider'; | |
type NumberMap = (x: number) => number; | |
type Map = number | boolean | { axisFromReal: NumberMap; realFromAxis: NumberMap }; | |
export default function LogarithmicRCSlider({ | |
map, | |
steps, | |
...props | |
}: Omit<SliderProps, 'step'> & { map?: Map; steps?: number; min?: number; max: number }): JSX.Element { | |
if (map !== false) { | |
// Default to simple log/exp | |
if (map === undefined) map = true; | |
// Simple log scale | |
if (map === true) { | |
map = { | |
axisFromReal: Math.log, | |
realFromAxis: Math.exp, | |
}; | |
} | |
// Log scale approximation that goes to zero | |
if (typeof map === 'number') { | |
if (map < 0) throw new RangeError('Negative maps are not currently allowed'); | |
// Helpful to allow logarithmic scales less than one that still go to zero | |
const scale = map || 1; | |
map = { | |
// Approximation of log/exp that goes to zero | |
axisFromReal: (real: number): number => { | |
real *= scale; | |
return real < 1 ? 0 : Math.log(real); | |
}, | |
realFromAxis: (axis: number): number => (axis && Math.exp(axis)) / scale, | |
}; | |
props.min = 0; | |
} | |
const { axisFromReal, realFromAxis } = map; | |
['max', 'min', 'value', 'defaultValue'].forEach(key => { | |
if (realFromAxis(axisFromReal(props[key])) !== props[key]) | |
throw new RangeError(`Supplied function map pair doesn't equal itself for ${key}: ${props[key]}`); | |
if (props[key] !== undefined) props[key] = axisFromReal(props[key]); | |
}); | |
if (steps) { | |
(props as SliderProps).step = (props.max - props.min) / steps; | |
} | |
if (props.marks) { | |
const next: typeof props.marks = {}; | |
for (const x in props.marks) { | |
next[axisFromReal(Number(x))] = props.marks[x]; | |
} | |
props.marks = next; | |
} | |
['onBeforeChange', 'onChange', 'onAfterChange'].forEach(key => { | |
if (props[key] === undefined) return; | |
const orig = props[key]; | |
props[key] = (value: number): void => orig(realFromAxis(value)); | |
}); | |
if (props.tipFormatter) { | |
props.tipFormatter = (v: number): ReturnType<typeof props.tipFormatter> => props.tipFormatter(realFromAxis(v)); | |
} | |
} | |
return <Slider {...props} />; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage
The usage is pretty straightforward for straight logrithmic scales.
All options for
rc-slider
'sSlider
are available exceptstep
because it doesn't makes sense on a logarithmic scale. If you want "stepyness", setsteps
instead.Does not generate
Marks
automatically but will correctly adjust their positions.Simple logarithmic scale
Modified Logarithmic - Goes to 0
Simply set
map
to a number.Really small maximums
If you want to go to zero and have a small maximum, we need some scaling to move that discontinuity to an even smaller scale.
Custom map function
Don't like
Math.log
andMath.exp
? Provide your own!If your provided functions don't match each other at key values, a
RangeError
will be thrown.Want to jump back to a linear scale?