feat(component): add slider ui component (#7879)

![CleanShot 2024-08-15 at 14 27 07@2x](https://github.com/user-attachments/assets/50299f44-6446-4ec8-a097-7d456ff67f46)
This commit is contained in:
JimmFly
2024-08-22 04:24:36 +00:00
parent 2e2a3af967
commit 03c4d56a91
7 changed files with 160 additions and 0 deletions

View File

@@ -43,6 +43,7 @@
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-slider": "^1.2.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.1.5",

View File

@@ -22,6 +22,7 @@ export * from './ui/popover';
export * from './ui/radio';
export * from './ui/scrollbar';
export * from './ui/skeleton';
export * from './ui/slider';
export * from './ui/switch';
export * from './ui/table';
export * from './ui/tabs';

View File

@@ -0,0 +1,57 @@
import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css';
export const trackStyle = style({
width: '100%',
height: '1px',
position: 'relative',
display: 'flex',
alignItems: 'center',
padding: '12px 0',
cursor: 'pointer',
});
export const fakeTrackStyle = style({
width: '100%',
height: '1px',
backgroundColor: cssVarV2('layer/insideBorder/border'),
position: 'relative',
display: 'flex',
justifyContent: 'space-between',
});
export const filledTrackStyle = style({
height: '100%',
backgroundColor: cssVarV2('icon/primary'),
borderRadius: '1px',
position: 'absolute',
top: '0',
left: '0',
});
export const thumbStyle = style({
width: '8px',
height: '8px',
backgroundColor: cssVarV2('icon/primary'),
borderRadius: '50%',
position: 'absolute',
top: '50%',
transform: 'translate(-50%, -50%)',
cursor: 'pointer',
});
export const nodeStyle = style({
width: '4px',
height: '4px',
border: '2px solid transparent',
backgroundColor: cssVarV2('layer/insideBorder/border'),
borderRadius: '50%',
position: 'absolute',
top: '50%',
cursor: 'pointer',
transform: 'translate(-50%, -50%)',
selectors: {
'&[data-active="true"]': {
backgroundColor: cssVarV2('icon/primary'),
},
},
});

View File

@@ -0,0 +1 @@
export * from './slider';

View File

@@ -0,0 +1,24 @@
import type { Meta, StoryFn } from '@storybook/react';
import { useState } from 'react';
import type { SliderProps } from './index';
import { Slider } from './index';
export default {
title: 'UI/Slider',
component: Slider,
} satisfies Meta<typeof Slider>;
const Template: StoryFn<SliderProps> = args => {
const [value, setValue] = useState<number[]>([0]);
return <Slider value={value} onValueChange={setValue} {...args} />;
};
export const Default: StoryFn<SliderProps> = Template.bind(undefined);
Default.args = {
min: 0,
max: 10,
width: 500,
step: 1,
nodes: [0, 5, 10],
};

View File

@@ -0,0 +1,75 @@
import * as Sliders from '@radix-ui/react-slider';
import { useRef } from 'react';
import * as styles from './index.css';
export interface SliderProps extends Sliders.SliderProps {
width?: number;
containerStyle?: React.CSSProperties;
rootStyle?: React.CSSProperties;
trackStyle?: React.CSSProperties;
rangeStyle?: React.CSSProperties;
thumbStyle?: React.CSSProperties;
noteStyle?: React.CSSProperties;
nodes?: number[]; // The values where the nodes should be placed
}
export const Slider = ({
value,
min = 0,
max = 10,
step,
width = 250,
nodes,
containerStyle,
rootStyle,
trackStyle,
rangeStyle,
thumbStyle,
noteStyle,
...props
}: SliderProps) => {
const sliderRef = useRef<HTMLDivElement>(null);
return (
<div style={{ ...containerStyle, width: width ? `${width}px` : undefined }}>
<Sliders.Root
value={value}
min={min}
max={max}
step={step}
style={rootStyle}
{...props}
>
<Sliders.Track className={styles.trackStyle} ref={sliderRef}>
<div className={styles.fakeTrackStyle} style={trackStyle}>
<Sliders.Range
className={styles.filledTrackStyle}
style={rangeStyle}
/>
</div>
{!!nodes &&
nodes.map((nodeValue, index) => (
<div
key={index}
className={styles.nodeStyle}
data-active={value && value[0] >= nodeValue}
style={{
left: `${((nodeValue - (min !== undefined ? min : 0)) / (max !== undefined ? max - (min !== undefined ? min : 0) : 1)) * 100}%`,
transform:
index === 0
? 'translateY(-50%)'
: index === nodes.length - 1
? 'translateY(-50%) translateX(-100%)'
: undefined,
...noteStyle,
}}
/>
))}
<Sliders.Thumb className={styles.thumbStyle} style={thumbStyle} />
</Sliders.Track>
</Sliders.Root>
</div>
);
};

View File

@@ -310,6 +310,7 @@ __metadata:
"@radix-ui/react-popover": "npm:^1.0.7"
"@radix-ui/react-radio-group": "npm:^1.1.3"
"@radix-ui/react-scroll-area": "npm:^1.0.5"
"@radix-ui/react-slider": "npm:^1.2.0"
"@radix-ui/react-slot": "npm:^1.1.0"
"@radix-ui/react-tabs": "npm:^1.1.0"
"@radix-ui/react-toast": "npm:^1.1.5"