Skip to content

Commit d6ed548

Browse files
authored
ENG-6161: Slider component (#10)
1 parent c532ba8 commit d6ed548

File tree

3 files changed

+249
-0
lines changed

3 files changed

+249
-0
lines changed

demo/demo/demo.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ def index() -> rx.Component:
1616
position="top-center",
1717
),
1818
),
19+
ui.slider(
20+
on_value_committed=lambda value: rx.toast.success(f"Value: {value}"),
21+
class_name="max-w-xs",
22+
),
1923
ui.select(
2024
items=[
2125
"Item 1",

reflex_ui/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"components.base.scroll_area": ["scroll_area"],
1010
"components.base.select": ["select"],
1111
"components.base.skeleton": ["skeleton"],
12+
"components.base.slider": ["slider"],
1213
"components.base.theme_switcher": ["theme_switcher"],
1314
}
1415

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
"""Custom Slider component."""
2+
3+
from typing import Literal
4+
5+
from reflex.components.component import Component, ComponentNamespace
6+
from reflex.event import EventHandler, passthrough_event_spec
7+
from reflex.utils.imports import ImportVar
8+
from reflex.vars import Var
9+
10+
from reflex_ui.components.base_ui import PACKAGE_NAME, BaseUIComponent
11+
from reflex_ui.utils.twmerge import cn
12+
13+
LiteralOrientation = Literal["horizontal", "vertical"]
14+
15+
on_value_event_spec = (
16+
passthrough_event_spec(int),
17+
passthrough_event_spec(float),
18+
passthrough_event_spec(list[int | float]),
19+
passthrough_event_spec(list[int]),
20+
passthrough_event_spec(list[float]),
21+
)
22+
23+
24+
class ClassNames:
25+
"""Class names for slider components."""
26+
27+
ROOT = "flex max-w-64 w-full touch-none items-center select-none"
28+
VALUE = "text-sm text-primary-11 font-medium"
29+
CONTROL = "flex items-center justify-center w-full"
30+
TRACK = "h-2 w-full rounded-full bg-secondary-4 select-none"
31+
INDICATOR = "absolute h-full rounded-full bg-primary-9 select-none"
32+
THUMB = "h-4 w-[0.575rem] rounded-[2px] bg-white outline outline-black/30 select-none box-shadow:[0_0_0_1px_rgba(0,0,0,1),0_1px_2px_rgba(0,0,0,.04)] data-[dragging]:h-5 transition-[height,scale] hover:h-4.5"
33+
34+
35+
class SliderBaseComponent(BaseUIComponent):
36+
"""Base component for slider components."""
37+
38+
library = f"{PACKAGE_NAME}/slider"
39+
40+
@property
41+
def import_var(self):
42+
"""Return the import variable for the slider component."""
43+
return ImportVar(tag="Slider", package_path="", install=False)
44+
45+
46+
class SliderRoot(SliderBaseComponent):
47+
"""Groups all parts of the slider. Renders a div element."""
48+
49+
tag = "Slider.Root"
50+
51+
# Identifies the field when a form is submitted.
52+
name: Var[str]
53+
54+
# The uncontrolled value of the slider when it's initially rendered. To render a controlled slider, use the value prop instead.
55+
default_value: Var[int | float | list[int | float]]
56+
57+
# The value of the slider. For ranged sliders, provide an array with two values.
58+
value: Var[int | float | list[int | float]]
59+
60+
# Callback function that is fired when the slider's value changed.
61+
on_value_change: EventHandler[on_value_event_spec]
62+
63+
# Callback function that is fired when the pointerup is triggered.
64+
on_value_committed: EventHandler[on_value_event_spec]
65+
66+
# Locale information for formatting.
67+
locale: Var[str]
68+
69+
# The granularity with which the slider can step through values. (A "discrete" slider.) The min prop serves as the origin for the valid values. We recommend (max - min) to be evenly divisible by the step. Defaults to 1.
70+
step: Var[float | int]
71+
72+
# The granularity with which the slider can step through values when using Page Up/Page Down or Shift + Arrow Up/Arrow Down. Defaults to 10.
73+
large_step: Var[float | int]
74+
75+
# The minimum steps between values in a range slider. Defaults to 0.
76+
min_steps_between_values: Var[float | int]
77+
78+
# The minimum allowed value of the slider. Should not be equal to max. Defaults to 0.
79+
min: Var[float | int]
80+
81+
# The maximum allowed value of the slider. Should not be equal to min. Defaults to 100.
82+
max: Var[float | int]
83+
84+
# Options to format the input value.
85+
format: Var[dict]
86+
87+
# Whether the slider is disabled.
88+
disabled: Var[bool]
89+
90+
# The component orientation. Defaults to "horizontal".
91+
orientation: Var[LiteralOrientation]
92+
93+
# A ref to access the hidden input element.
94+
input_ref: Var[str]
95+
96+
# The render prop
97+
render_: Var[Component]
98+
99+
@classmethod
100+
def create(cls, *children, **props) -> Component:
101+
"""Create the slider root component."""
102+
cls.set_class_name(ClassNames.ROOT, props)
103+
return super().create(*children, **props)
104+
105+
106+
class SliderValue(SliderBaseComponent):
107+
"""Displays the current value of the slider as text. Renders an output element."""
108+
109+
tag = "Slider.Value"
110+
111+
# The render prop
112+
render_: Var[Component]
113+
114+
@classmethod
115+
def create(cls, *children, **props) -> Component:
116+
"""Create the slider value component."""
117+
cls.set_class_name(ClassNames.VALUE, props)
118+
return super().create(*children, **props)
119+
120+
121+
class SliderControl(SliderBaseComponent):
122+
"""The clickable, interactive part of the slider. Renders a div element."""
123+
124+
tag = "Slider.Control"
125+
126+
# The render prop
127+
render_: Var[Component]
128+
129+
@classmethod
130+
def create(cls, *children, **props) -> Component:
131+
"""Create the slider control component."""
132+
cls.set_class_name(ClassNames.CONTROL, props)
133+
return super().create(*children, **props)
134+
135+
136+
class SliderTrack(SliderBaseComponent):
137+
"""Contains the slider indicator and represents the entire range of the slider. Renders a div element."""
138+
139+
tag = "Slider.Track"
140+
141+
# The render prop
142+
render_: Var[Component]
143+
144+
@classmethod
145+
def create(cls, *children, **props) -> Component:
146+
"""Create the slider track component."""
147+
cls.set_class_name(ClassNames.TRACK, props)
148+
return super().create(*children, **props)
149+
150+
151+
class SliderIndicator(SliderBaseComponent):
152+
"""Visualizes the current value of the slider. Renders a div element."""
153+
154+
tag = "Slider.Indicator"
155+
156+
# The render prop
157+
render_: Var[Component]
158+
159+
@classmethod
160+
def create(cls, *children, **props) -> Component:
161+
"""Create the slider indicator component."""
162+
cls.set_class_name(ClassNames.INDICATOR, props)
163+
return super().create(*children, **props)
164+
165+
166+
class SliderThumb(SliderBaseComponent):
167+
"""The draggable part of the slider at the tip of the indicator. Renders a div element."""
168+
169+
tag = "Slider.Thumb"
170+
171+
# Accepts a function which returns a string value that provides a user-friendly name for the input associated with the thumb.
172+
get_aria_label: Var[str]
173+
174+
# Accepts a function which returns a string value that provides a user-friendly name for the current value of the slider. This is important for screen reader users.
175+
get_aria_value_text: Var[str]
176+
177+
# Whether the thumb should ignore user interaction.
178+
disabled: Var[bool]
179+
180+
# The render prop
181+
render_: Var[Component]
182+
183+
@classmethod
184+
def create(cls, *children, **props) -> Component:
185+
"""Create the slider thumb component."""
186+
cls.set_class_name(ClassNames.THUMB, props)
187+
return super().create(*children, **props)
188+
189+
190+
class HighLevelSlider(SliderRoot):
191+
"""High-level wrapper for the Slider component."""
192+
193+
# Class name for the indicator
194+
indicator_class_name: Var[str]
195+
196+
# Class name for the track
197+
track_class_name: Var[str]
198+
199+
# Class name for the control
200+
control_class_name: Var[str]
201+
202+
@classmethod
203+
def create(cls, **props) -> Component:
204+
"""Create a complete slider component.
205+
206+
Args:
207+
**props: Additional properties to apply to the slider component.
208+
209+
Returns:
210+
The slider component.
211+
"""
212+
# Extract custom class names for the indicator, track, and control
213+
indicator_class_name = props.pop("indicator_class_name", "")
214+
track_class_name = props.pop("track_class_name", "")
215+
control_class_name = props.pop("control_class_name", "")
216+
217+
return SliderRoot.create(
218+
SliderControl.create(
219+
SliderTrack.create(
220+
SliderIndicator.create(
221+
class_name=cn(ClassNames.INDICATOR, indicator_class_name)
222+
),
223+
SliderThumb.create(),
224+
class_name=cn(ClassNames.TRACK, track_class_name),
225+
),
226+
class_name=cn(ClassNames.CONTROL, control_class_name),
227+
),
228+
**props,
229+
)
230+
231+
232+
class Slider(ComponentNamespace):
233+
"""Namespace for Slider components."""
234+
235+
root = staticmethod(SliderRoot.create)
236+
value = staticmethod(SliderValue.create)
237+
control = staticmethod(SliderControl.create)
238+
track = staticmethod(SliderTrack.create)
239+
indicator = staticmethod(SliderIndicator.create)
240+
thumb = staticmethod(SliderThumb.create)
241+
__call__ = staticmethod(HighLevelSlider.create)
242+
243+
244+
slider = Slider()

0 commit comments

Comments
 (0)