mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-23 20:18:42 +08:00
feat(component): add slider ui component (#7879)

This commit is contained in:
@@ -43,6 +43,7 @@
|
|||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.0.7",
|
||||||
"@radix-ui/react-radio-group": "^1.1.3",
|
"@radix-ui/react-radio-group": "^1.1.3",
|
||||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
"@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-slot": "^1.1.0",
|
||||||
"@radix-ui/react-tabs": "^1.1.0",
|
"@radix-ui/react-tabs": "^1.1.0",
|
||||||
"@radix-ui/react-toast": "^1.1.5",
|
"@radix-ui/react-toast": "^1.1.5",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export * from './ui/popover';
|
|||||||
export * from './ui/radio';
|
export * from './ui/radio';
|
||||||
export * from './ui/scrollbar';
|
export * from './ui/scrollbar';
|
||||||
export * from './ui/skeleton';
|
export * from './ui/skeleton';
|
||||||
|
export * from './ui/slider';
|
||||||
export * from './ui/switch';
|
export * from './ui/switch';
|
||||||
export * from './ui/table';
|
export * from './ui/table';
|
||||||
export * from './ui/tabs';
|
export * from './ui/tabs';
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './slider';
|
||||||
@@ -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],
|
||||||
|
};
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -310,6 +310,7 @@ __metadata:
|
|||||||
"@radix-ui/react-popover": "npm:^1.0.7"
|
"@radix-ui/react-popover": "npm:^1.0.7"
|
||||||
"@radix-ui/react-radio-group": "npm:^1.1.3"
|
"@radix-ui/react-radio-group": "npm:^1.1.3"
|
||||||
"@radix-ui/react-scroll-area": "npm:^1.0.5"
|
"@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-slot": "npm:^1.1.0"
|
||||||
"@radix-ui/react-tabs": "npm:^1.1.0"
|
"@radix-ui/react-tabs": "npm:^1.1.0"
|
||||||
"@radix-ui/react-toast": "npm:^1.1.5"
|
"@radix-ui/react-toast": "npm:^1.1.5"
|
||||||
|
|||||||
Reference in New Issue
Block a user