New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MultiRangeSlider component #2407
Conversation
Thanks for your interest in palantir/blueprint, @jscheiny! Before we can accept your pull request, you need to sign our contributor license agreement - just visit https://cla.palantir.com/ and follow the instructions. Once you sign, I'll automatically update this pull request. |
border-bottom-right-radius: 0; | ||
} | ||
|
||
&.pt-intent-primary { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a sass wizard, and I imagine that there's a much nicer way of doing these intent blocks. Any thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can loop through all the intents. check out how buttons, callouts, and other intent-supported components do it
handle: (el: HTMLSpanElement) => (this.handleElement = el), | ||
handle: (el: HTMLSpanElement) => { | ||
this.handleElement = el; | ||
this.forceUpdate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is here because when the number of handles changes, these handles are re-instantiated. When that happens the first render of the component does not have the ref captured and thus incorrectly positions the handle. This fixes by immediately re-rendering once the ref is captured, not sure if there's a nicer way to handle (haha) this. Otherwise, will add a comment to this effect.
one hell of a first time contrib, @jscheiny 🤯 😍 |
I like to make an entrance 😄 |
Use children instead of array of propsPreview: documentation | landing | table |
Some minor styling issues I found: When a lower handle with its above track having no intent is moved to the end of the slider, the border radius can be seen through slightly: Same problem at the other end (upper handle with no below track color): @llorca This is pretty minor, worth fixing? |
packages/core/src/common/classes.ts
Outdated
export const SLIDER_PROGRESS = "pt-slider-progress"; | ||
export const RANGE_SLIDER = "pt-range-slider"; | ||
export const MULTI_RANGE_SLIDER = "pt-multi-range-slider"; | ||
export const LOWER = "pt-lower"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about start
and end
? we use this vocabulary in other places already (more generic), and it seems to make sense as well here. In addition, lower
and upper
imply a vertical axis, whereas sliders can be either horizontal or vertical
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Also changed "above" and "below" to "after" and "before"
@jscheiny yes please fix. an easy way would be to shave 1-2px off the max left or right a handle can be, so that it's always on top of the track border radii. I would not suggest modifying the border radius depending on the position of the handles |
Fix lint?Preview: documentation | landing | table |
@jscheiny even though the API isn't locked down, this is clearly an improvement and we'll work with you to get it in. Can you set up a quick docs section for |
Merge remote-tracking branch 'upstream/develop' into review/js/multi-range-sliderPreview: documentation | landing | table |
Simplify intent coloringPreview: documentation | landing | table |
@llorca I'd be happy to add an example. However, I can't seem to get markdown changes to compile locally so I can't test that what I'm doing works. I'm running with |
@jscheiny hmm yeah I have this issue sometimes too. what about |
I was able to do it by running Anyways @llorca there should be a multi-range slider example now. |
Example componentPreview: documentation | landing | table |
@jscheiny run |
Remove importPreview: documentation | landing | table |
|
||
export interface ISliderHandleProps { | ||
value: number; | ||
trackIntentAbove?: Intent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
className?
and trackClassName?
would allow custom styling (like non-intent track colors)
handle: (el: HTMLSpanElement) => { | ||
this.handleElement = el; | ||
// TODO If this ends up being the approved way of working, only call this function on the first ref and add | ||
// a comment explaning why. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it's just on the first ref, then it should be in componentDidMount
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do that, did you see my other comment on why this is necessary / have any thoughts on how to remedy?
let minValue = values[0]; | ||
let minArg = argFn(minValue); | ||
|
||
for (let index = 1; index < values.length; index++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Math.min(...spread an array)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I needed something equivalent of _.minBy
, i.e. find the min of an array as determined by some function.
).filter(child => child !== null); | ||
} | ||
|
||
function compare(left: number, right: number) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used once, i suggest inlining the subtraction
import { ISliderHandleProps, SliderHandle } from "./sliderHandle"; | ||
|
||
export interface IMultiRangeSliderProps extends ICoreSliderProps { | ||
children?: React.ReactNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not necessary as it's the same as React's definition. what would be awesome, since it's validated below, is to declare something along the lines of children?: React.ReactElement<ISliderHandleProps>[]
|
||
export type SliderHandleType = "full" | "start" | "end"; | ||
|
||
export interface ISliderHandleProps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
className?
and trackClassName?
would allow custom styling (like non-intent track colors)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what className
styling on the handle itself would do. Happy to do the class name suggestion, what do you think trackAboveClassName
and trackBelowClassName
? I found it pretty important in my usage of this component to be able to specify for certain handles the color on the track on one side or the other of it.
Should we still have intent
props or just expect people to pass in Classes.INTENT_PRIMARY
etc.?
type?: SliderHandleType; | ||
} | ||
|
||
export class SliderHandle extends AbstractPureComponent<ISliderHandleProps> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can just use React.PureComponent
here as you're not using any of the APC
features
Address commentsPreview: documentation | landing | table |
Refactor slider testsPreview: documentation | landing | table |
Merge branch 'develop' into js/multi-range-sliderPreview: documentation | landing | table |
… into review/js/multi-range-slider
Merge branch 'js/multi-range-slider' of github.com:jscheiny/blueprint into review/js/multi-range-sliderPreview: documentation | landing | table |
Add default track intentPreview: documentation | landing | table |
Fix vertical track border radiiPreview: documentation | landing | table |
Fix lintPreview: documentation | landing | table |
@jscheiny mind re-opening this as a branch in this repo so I can contribute as well? |
Closing this in favor of #2536 |
Checklist
Changes proposed in this pull request:
This adds a very general
MultiRangeSlider
component that ultimately should be able to subsume theSlider
andRangeSlider
components.Notes about the component API:
value
- where it gets placed on the slidertype
- specifies how the slider handle should be displayed"full"
- handle appears like it does in aSlider
(default)"lower"
- handle appears like the first slider in aRangeSlider
"upper"
- handle appears like the second slider in aRangeSlider
trackIntent{Above,Below}
specifies the color of the track fill above and below (numerically) of the given handle.Handles are always displayed in ascending numeric order and the values that are returned by
onChange
andonRelease
are always sorted.In addition,
MultiRangeSlider
takes adefaultTrackIntent
prop which is used in the resolution of how to render the color of the track between two given handles. The way that this is resolved is as follows (and is one of my concerns about the API).Important: Note that there is a difference between the
NONE
intent and the absence of an intent entirely (undefined
). The former will render a gray track between the two handles and the latter will fall-through to the next color in the hierarchy above.I previously allowed the specification of colors instead of intents but that didn't seem very blueprint-y.
Reviewers should focus on:
I have explicitly not written tests/docs for this yet, as I would first like to iterate on the API as I'm not convinced what I've written is particularly nice (worked for our use case but don't know if it generalizes well). Once we've iterated and nailed down an API I will gladly write tests and docs for this component.
I've written some questions in the code, but one additional question I have is should this component live in
labs
first to incubate before moving intocore
?Not addressed in this PR (requires follow-up):
To keep this PR simple(r) and easy(er) to review, I have not made many changes to the existing slider components but in 1-2 follow up PRs I would like to:
Slider
andRangeSlider
to be simple wrappers aroundMultiRangeSlider
CoreSlider
and refactor intoMultiRangeSlider
The second one is questionable, especially if there are plans to add a slider that does not fit into the multi range slider model.
Screenshots
Two tailed slider:
Disabled:
One tailed:
Weird:
Vertical:
In action: