Skip to content

Commit b44b2d9

Browse files
feat: slider style update and add thumb size variant (#533)
* feat: update slider changes * feat: add slider thumb small size * fix: use tokens instead of px value
1 parent a222873 commit b44b2d9

File tree

5 files changed

+119
-53
lines changed

5 files changed

+119
-53
lines changed
Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,98 @@
1-
"use client";
1+
'use client';
22

3-
import { getPropsString } from "@/lib/utils";
3+
import { getPropsString } from '@/lib/utils';
44

55
export const getCode = (props: any) => {
66
return `<Slider${getPropsString(props)}/>`;
77
};
88

99
export const playground = {
10-
type: "playground",
10+
type: 'playground',
1111
controls: {
12-
defaultValue: { type: "number", initialValue: 50 },
13-
min: { type: "number", defaultValue: 0, min: 0, max: 99 },
14-
max: { type: "number", defaultValue: 100, min: 1, max: 100 },
15-
step: { type: "number", defaultValue: 1, min: 0, max: 100 },
16-
label: { type: "text", initialValue: "Slider Label" },
12+
defaultValue: { type: 'number', initialValue: 50 },
13+
thumbSize: {
14+
type: 'select',
15+
initialValue: 'large',
16+
options: ['small', 'large']
17+
},
18+
min: { type: 'number', defaultValue: 0, min: 0, max: 99 },
19+
max: { type: 'number', defaultValue: 100, min: 1, max: 100 },
20+
step: { type: 'number', defaultValue: 1, min: 0, max: 100 },
21+
label: { type: 'text', initialValue: 'Slider Label' }
1722
},
18-
getCode,
23+
getCode
1924
};
2025

2126
export const variantDemo = {
22-
type: "code",
27+
type: 'code',
2328
tabs: [
2429
{
25-
name: "Single",
26-
code: `<Slider variant="single" label="Value" defaultValue={50} />`,
30+
name: 'Single',
31+
code: `<Slider variant="single" label="Value" defaultValue={50} />`
2732
},
2833
{
29-
name: "Range",
30-
code: `<Slider variant="range" label={["Min", "Max"]} defaultValue={[20, 80]} />`,
31-
},
32-
],
34+
name: 'Range',
35+
code: `<Slider variant="range" label={["Min", "Max"]} defaultValue={[20, 80]} />`
36+
}
37+
]
3338
};
3439
export const controlDemo = {
35-
type: "code",
40+
type: 'code',
3641
tabs: [
3742
{
38-
name: "Single",
43+
name: 'Single',
3944
code: `function ControlledRangeSlider() {
4045
const [value, setValue] = React.useState(50);
41-
46+
4247
return (
4348
<Flex direction="column" gap="medium" align="center" style={{ width: "400px" }}>
44-
<Slider
45-
variant="single"
46-
value={value}
49+
<Slider
50+
variant="single"
51+
value={value}
4752
label="Value"
4853
onChange={(newValue) => setValue(newValue as number)}
4954
/>
5055
<Text>Value {value}</Text>
5156
</Flex>
5257
);
53-
}`,
58+
}`
5459
},
5560
{
56-
name: "Range",
61+
name: 'Range',
5762
code: `function ControlledRangeSlider() {
5863
const [value, setValue] = React.useState([25, 75]);
59-
64+
6065
return (
6166
<Flex direction="column" gap="medium" align="center" style={{ width: "400px" }}>
62-
<Slider
63-
variant="range"
64-
value={value}
67+
<Slider
68+
variant="range"
69+
value={value}
6570
label={["Lower", "Upper"]}
6671
onChange={(newValue) => setValue(newValue as [number, number])}
6772
/>
6873
<Text>Lower {value[0]}</Text>
6974
<Text>Upper {value[1]}</Text>
7075
</Flex>
7176
);
72-
}`,
73-
},
74-
],
77+
}`
78+
}
79+
]
80+
};
81+
82+
export const thumbSizeDemo = {
83+
type: 'code',
84+
code: `<Flex direction="column" gap="extra-large" align="center" style={{ width: "400px" }}>
85+
<Slider
86+
variant="single"
87+
label="Large Thumb"
88+
defaultValue={50}
89+
thumbSize="large"
90+
/>
91+
<Slider
92+
variant="single"
93+
label="Small Thumb"
94+
defaultValue={50}
95+
thumbSize="small"
96+
/>
97+
</Flex>`
7598
};

apps/www/src/content/docs/components/slider/index.mdx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
22
title: Slider
33
description: A control that allows users to select a value or range from a given range.
4+
tag: update
45
---
56

6-
import { playground, variantDemo, controlDemo } from "./demo.ts";
7+
import { playground, variantDemo, controlDemo, thumbSizeDemo } from "./demo.ts";
78

89
<Demo data={playground} />
910

@@ -29,6 +30,12 @@ A controlled slider that maintains and updates its state through React's useStat
2930

3031
<Demo data={controlDemo} />
3132

33+
### Thumb Size
34+
35+
Different thumb sizes for various use cases and visual preferences.
36+
37+
<Demo data={thumbSizeDemo} />
38+
3239
## Accessibility
3340

3441
The Slider component follows WAI-ARIA guidelines for the [Slider Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/slider/).

apps/www/src/content/docs/components/slider/props.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export interface SliderProps {
22
/** The type of slider. */
3-
variant?: "single" | "range";
3+
variant?: 'single' | 'range';
44

55
/** Controlled value - number for single, [number, number] for range. */
66
value?: number | [number, number];
@@ -32,6 +32,12 @@ export interface SliderProps {
3232
*/
3333
label?: string | [string, string];
3434

35+
/**
36+
* Size of the slider thumb.
37+
* @default "large"
38+
*/
39+
thumbSize?: 'small' | 'large';
40+
3541
/** Callback when value changes. */
3642
onChange?: (value: number | [number, number]) => void;
3743

packages/raystack/components/slider/slider.module.css

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,15 @@
1414
.track {
1515
position: relative;
1616
flex-grow: 1;
17-
height: var(--rs-space-1);
17+
height: var(--rs-space-2);
1818
background-color: var(--rs-color-background-neutral-secondary);
19-
border-radius: var(--rs-radius-full);
20-
margin: 0 12px;
19+
margin: 0 var(--rs-space-4);
2120
}
2221

2322
.range {
2423
position: absolute;
2524
height: 100%;
2625
background-color: var(--rs-color-background-accent-emphasis);
27-
border-radius: var(--rs-radius-full);
2826
}
2927

3028
.thumb {
@@ -35,7 +33,6 @@
3533
justify-content: center;
3634
outline: none;
3735
transform: translate(-50%, -50%);
38-
top: 50%;
3936
}
4037

4138
.thumb:active {
@@ -47,35 +44,48 @@
4744
outline: none;
4845
}
4946

47+
.thumb:hover {
48+
border-color: var(--rs-color-border-accent-emphasis-hover);
49+
}
50+
5051
.thumb svg {
51-
width: 24px;
52-
height: 24px;
52+
width: 32px;
53+
height: 28px;
5354
fill: var(--rs-color-background-base-primary);
55+
margin-top: var(--rs-space-1);
5456
}
5557

5658
.thumb:hover svg {
5759
fill: var(--rs-color-background-base-secondary);
5860
}
5961

60-
.thumb:hover {
61-
border-color: var(--rs-color-border-accent-emphasis-hover);
62+
.thumbSmall {
63+
width: 8px;
64+
height: 16px;
65+
border-radius: var(--rs-radius-full);
66+
background-color: var(--rs-color-background-base-primary);
67+
border: 1px solid var(--rs-color-border-base-tertiary);
68+
box-shadow: var(--rs-shadow-soft);
6269
}
6370

6471
.label {
6572
position: absolute;
66-
top: calc(-1 * var(--rs-space-7));
73+
top: calc(-1 * (var(--rs-space-7) + 1px));
6774
left: 50%;
68-
padding: var(--rs-space-1) var(--rs-space-2);
75+
padding: var(--rs-space-2);
6976
color: var(--rs-color-foreground-base-primary);
7077
background-color: var(--rs-color-background-base-primary);
7178
border: 0.5px solid var(--rs-color-border-base-primary);
7279
border-radius: var(--rs-radius-2);
73-
font-size: var(--rs-font-size-mini);
7480
transform: translateX(-50%);
7581
white-space: nowrap;
7682
box-shadow: var(--rs-shadow-soft);
7783
}
7884

85+
.thumb[data-size="small"] .label {
86+
top: calc(-1 * (var(--rs-space-7)));
87+
}
88+
7989
.slider-variant-single .range {
8090
left: 0;
8191
}

packages/raystack/components/slider/slider.tsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
'use client';
22

3-
import { type VariantProps, cva } from 'class-variance-authority';
3+
import { type VariantProps, cva, cx } from 'class-variance-authority';
44
import { Slider as SliderPrimitive } from 'radix-ui';
5-
import * as React from 'react';
6-
import { type ComponentPropsWithoutRef } from 'react';
5+
import {
6+
type ComponentPropsWithoutRef,
7+
type ElementRef,
8+
forwardRef
9+
} from 'react';
10+
import { Text } from '../text';
711
import styles from './slider.module.css';
812
import { ThumbIcon } from './thumb';
913

@@ -34,10 +38,11 @@ export interface SliderProps
3438
onChange?: (value: number | [number, number]) => void;
3539
'aria-label'?: string;
3640
'aria-valuetext'?: string;
41+
thumbSize?: 'small' | 'large';
3742
}
3843

39-
export const Slider = React.forwardRef<
40-
React.ElementRef<typeof SliderPrimitive.Root>,
44+
export const Slider = forwardRef<
45+
ElementRef<typeof SliderPrimitive.Root>,
4146
SliderProps
4247
>(
4348
(
@@ -53,11 +58,13 @@ export const Slider = React.forwardRef<
5358
onChange,
5459
'aria-label': ariaLabel,
5560
'aria-valuetext': ariaValueText,
61+
thumbSize = 'large',
5662
...props
5763
},
5864
ref
5965
) => {
6066
const isRange = variant === 'range';
67+
const isThumbSmall = thumbSize === 'small';
6168
const defaultVal = isRange
6269
? (defaultValue as [number, number]) || [min, max]
6370
: [(defaultValue as number) || min];
@@ -101,14 +108,27 @@ export const Slider = React.forwardRef<
101108
{defaultVal.map((_, i) => (
102109
<SliderPrimitive.Thumb
103110
key={i}
104-
className={styles.thumb}
111+
className={cx(styles.thumb)}
105112
asChild
106113
aria-label={getLabel(i) || `Thumb ${i + 1}`}
107114
aria-valuetext={getAriaValueText(i)}
115+
data-size={thumbSize}
108116
>
109117
<div>
110-
<ThumbIcon />
111-
{getLabel(i) && <div className={styles.label}>{getLabel(i)}</div>}
118+
{isThumbSmall ? (
119+
<div className={styles.thumbSmall} />
120+
) : (
121+
<ThumbIcon />
122+
)}
123+
{getLabel(i) && (
124+
<Text
125+
className={styles.label}
126+
size={isThumbSmall ? 'micro' : 'mini'}
127+
weight='medium'
128+
>
129+
{getLabel(i)}
130+
</Text>
131+
)}
112132
</div>
113133
</SliderPrimitive.Thumb>
114134
))}

0 commit comments

Comments
 (0)