parent
88770462ab
commit
b66666a087
1 changed files with 206 additions and 0 deletions
@ -0,0 +1,206 @@ |
|||||||
|
import { useState } from 'react'; |
||||||
|
import { |
||||||
|
Box, |
||||||
|
inputLabelClasses as muiInputLabelClasses, |
||||||
|
outlinedInputClasses as muiOutlinedInputClasses, |
||||||
|
Slider as MUISlider, |
||||||
|
sliderClasses as muiSliderClasses, |
||||||
|
SliderProps as MUISliderProps, |
||||||
|
TypographyProps as MUITypographyProps, |
||||||
|
} from '@mui/material'; |
||||||
|
|
||||||
|
import { BORDER_RADIUS, GREY } from '../lib/consts/DEFAULT_THEME'; |
||||||
|
|
||||||
|
import OutlinedInput from './OutlinedInput'; |
||||||
|
import OutlinedInputLabel from './OutlinedInputLabel'; |
||||||
|
import { BodyText } from './Text'; |
||||||
|
|
||||||
|
type SliderOnBlur = Exclude<MUISliderProps['onBlur'], undefined>; |
||||||
|
type SliderOnChange = Exclude<MUISliderProps['onChange'], undefined>; |
||||||
|
type SliderOnFocus = Exclude<MUISliderProps['onFocus'], undefined>; |
||||||
|
type SliderValue = Exclude<MUISliderProps['value'], undefined>; |
||||||
|
|
||||||
|
type SliderOptionalProps = { |
||||||
|
isAllowTextInput?: boolean; |
||||||
|
labelId?: string; |
||||||
|
labelProps?: MUITypographyProps; |
||||||
|
sliderProps?: MUISliderProps; |
||||||
|
}; |
||||||
|
|
||||||
|
type SliderProps = { |
||||||
|
label: string; |
||||||
|
value: SliderValue; |
||||||
|
} & SliderOptionalProps; |
||||||
|
|
||||||
|
const SLIDER_DEFAULT_PROPS: Required<SliderOptionalProps> = { |
||||||
|
isAllowTextInput: false, |
||||||
|
labelId: '', |
||||||
|
labelProps: {}, |
||||||
|
sliderProps: {}, |
||||||
|
}; |
||||||
|
|
||||||
|
const createInputLabelDecorator = (label: string, isFocused: boolean) => { |
||||||
|
const borderColor = GREY; |
||||||
|
const borderStyle = 'solid'; |
||||||
|
const borderWidth = isFocused ? '2px 0 0 0' : '1px 0 0 0'; |
||||||
|
const content = '""'; |
||||||
|
const opacity = isFocused ? '1' : '0.3'; |
||||||
|
|
||||||
|
return ( |
||||||
|
<Box |
||||||
|
sx={{ |
||||||
|
display: 'flex', |
||||||
|
flexDirection: 'row', |
||||||
|
position: 'absolute', |
||||||
|
width: '24em', |
||||||
|
|
||||||
|
'> :last-child': { |
||||||
|
flexGrow: 1, |
||||||
|
}, |
||||||
|
}} |
||||||
|
> |
||||||
|
<Box |
||||||
|
sx={{ |
||||||
|
borderColor, |
||||||
|
borderStyle, |
||||||
|
borderWidth, |
||||||
|
content, |
||||||
|
opacity, |
||||||
|
width: '.6em', |
||||||
|
}} |
||||||
|
/> |
||||||
|
<BodyText |
||||||
|
sx={{ |
||||||
|
fontSize: '.75em', |
||||||
|
margin: isFocused ? '0 1em 0 1em' : '0 .6em 0 .4em', |
||||||
|
visibility: 'hidden', |
||||||
|
}} |
||||||
|
text={label} |
||||||
|
/> |
||||||
|
<Box |
||||||
|
sx={{ |
||||||
|
borderColor, |
||||||
|
borderStyle, |
||||||
|
borderWidth, |
||||||
|
content, |
||||||
|
opacity, |
||||||
|
}} |
||||||
|
/> |
||||||
|
</Box> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const createOutlinedInput = ( |
||||||
|
sliderValue: SliderValue, |
||||||
|
isFocused: boolean, |
||||||
|
onFocus: SliderOnFocus, |
||||||
|
onBlur: SliderOnBlur, |
||||||
|
) => ( |
||||||
|
<OutlinedInput |
||||||
|
{...{ |
||||||
|
className: isFocused ? muiOutlinedInputClasses.focused : '', |
||||||
|
onBlur, |
||||||
|
onFocus, |
||||||
|
value: sliderValue, |
||||||
|
}} |
||||||
|
/> |
||||||
|
); |
||||||
|
|
||||||
|
const Slider = ({ |
||||||
|
isAllowTextInput, |
||||||
|
label, |
||||||
|
labelId, |
||||||
|
labelProps, |
||||||
|
sliderProps, |
||||||
|
value, |
||||||
|
}: SliderProps): JSX.Element => { |
||||||
|
const { sx: labelSx } = labelProps ?? SLIDER_DEFAULT_PROPS.labelProps; |
||||||
|
const { |
||||||
|
onChange: sliderChangeCallback, |
||||||
|
sx: sliderSx, |
||||||
|
valueLabelDisplay: sliderValueLabelDisplay, |
||||||
|
} = sliderProps ?? SLIDER_DEFAULT_PROPS.sliderProps; |
||||||
|
|
||||||
|
const [sliderValue, setSliderValue] = useState<SliderValue>(value); |
||||||
|
const [isFocused, setIsFocused] = useState<boolean>(false); |
||||||
|
|
||||||
|
const handleLocalSliderBlur: SliderOnBlur = () => { |
||||||
|
setIsFocused(false); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleLocalSliderChange: SliderOnChange = (event, newValue) => { |
||||||
|
setSliderValue(newValue); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleLocalSliderFocus: SliderOnFocus = () => { |
||||||
|
setIsFocused(true); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleSliderChange = sliderChangeCallback |
||||||
|
? (...args: Parameters<SliderOnChange>) => { |
||||||
|
handleLocalSliderChange(...args); |
||||||
|
sliderChangeCallback(...args); |
||||||
|
} |
||||||
|
: handleLocalSliderChange; |
||||||
|
|
||||||
|
return ( |
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column' }}> |
||||||
|
<OutlinedInputLabel |
||||||
|
{...{ |
||||||
|
className: isFocused ? muiInputLabelClasses.focused : '', |
||||||
|
id: labelId, |
||||||
|
sx: { |
||||||
|
...labelSx, |
||||||
|
}, |
||||||
|
}} |
||||||
|
> |
||||||
|
{label} |
||||||
|
</OutlinedInputLabel> |
||||||
|
{createInputLabelDecorator(label, isFocused)} |
||||||
|
<Box |
||||||
|
sx={{ |
||||||
|
alignItems: 'center', |
||||||
|
display: 'flex', |
||||||
|
flexDirection: 'row', |
||||||
|
'> :first-child': { flexGrow: 1 }, |
||||||
|
}} |
||||||
|
> |
||||||
|
<MUISlider |
||||||
|
{...{ |
||||||
|
'aria-labelledby': labelId, |
||||||
|
onBlur: handleLocalSliderBlur, |
||||||
|
onChange: handleSliderChange, |
||||||
|
onFocus: handleLocalSliderFocus, |
||||||
|
sx: { |
||||||
|
color: GREY, |
||||||
|
marginLeft: '13px', |
||||||
|
marginRight: '26px', |
||||||
|
|
||||||
|
[`& .${muiSliderClasses.thumb}`]: { |
||||||
|
borderRadius: BORDER_RADIUS, |
||||||
|
transform: 'translate(-50%, -50%) rotate(45deg)', |
||||||
|
}, |
||||||
|
|
||||||
|
...sliderSx, |
||||||
|
}, |
||||||
|
value: sliderValue, |
||||||
|
valueLabelDisplay: sliderValueLabelDisplay, |
||||||
|
}} |
||||||
|
/> |
||||||
|
{isAllowTextInput && |
||||||
|
createOutlinedInput( |
||||||
|
sliderValue, |
||||||
|
isFocused, |
||||||
|
handleLocalSliderFocus, |
||||||
|
handleLocalSliderBlur, |
||||||
|
)} |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
Slider.defaultProps = SLIDER_DEFAULT_PROPS; |
||||||
|
|
||||||
|
export type { SliderProps }; |
||||||
|
|
||||||
|
export default Slider; |
Loading…
Reference in new issue