From 59d61cdfdd2de9c68ee8f4d44b5e17c44073b61f Mon Sep 17 00:00:00 2001 From: Jaya Krishna Date: Fri, 27 Sep 2024 14:20:34 +0530 Subject: [PATCH] pass props when a thumb is selected on the radix slider --- .../backgrounds/gradient-control.stories.tsx | 3 + .../sections/backgrounds/gradient-control.tsx | 69 +++++++++++++++---- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/apps/builder/app/builder/features/style-panel/sections/backgrounds/gradient-control.stories.tsx b/apps/builder/app/builder/features/style-panel/sections/backgrounds/gradient-control.stories.tsx index 07a95bea6605..b84427104399 100644 --- a/apps/builder/app/builder/features/style-panel/sections/backgrounds/gradient-control.stories.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/backgrounds/gradient-control.stories.tsx @@ -22,6 +22,7 @@ export const GradientWithoutAngle = () => { onChange={(value) => { setGradient(reconstructLinearGradient(value)); }} + onThumbSelected={() => {}} /> {gradient} @@ -40,6 +41,7 @@ export const GradientWithAngleAndHints = () => { onChange={(value) => { setGradient(reconstructLinearGradient(value)); }} + onThumbSelected={() => {}} /> {gradient} @@ -58,6 +60,7 @@ export const GradientWithSideOrCorner = () => { onChange={(value) => { setGradient(reconstructLinearGradient(value)); }} + onThumbSelected={() => {}} /> {gradient} diff --git a/apps/builder/app/builder/features/style-panel/sections/backgrounds/gradient-control.tsx b/apps/builder/app/builder/features/style-panel/sections/backgrounds/gradient-control.tsx index 9a9a0be39e52..3115d5821049 100644 --- a/apps/builder/app/builder/features/style-panel/sections/backgrounds/gradient-control.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/backgrounds/gradient-control.tsx @@ -16,6 +16,7 @@ extend([mixPlugin]); type GradientControlProps = { gradient: ParsedGradient; onChange: (value: ParsedGradient) => void; + onThumbSelected: (index: number, stop: GradientStop) => void; }; const defaultAngle: UnitValue = { @@ -27,6 +28,7 @@ const defaultAngle: UnitValue = { export const GradientControl = (props: GradientControlProps) => { const [stops, setStops] = useState>(props.gradient.stops); const [selectedStop, setSelectedStop] = useState(); + const [isHoveredOnStop, setIsHoveredOnStop] = useState(false); const positions = stops .map((stop) => stop.position?.value) .filter((item) => item !== undefined); @@ -80,24 +82,36 @@ export const GradientControl = (props: GradientControlProps) => { [stops, selectedStop] ); - const handlePointerDown = useCallback( - (event: React.MouseEvent) => { - if (event.target === undefined || event.target === null) { - return; - } - - // radix-slider automatically brings the closest thumb to the clicked position. - // But, we want it be prevented. So, we can add a new color-stop where the user is cliked. - // And handle the even for scrubing when the user is dragging the thumb. + const isStopExistsAtPosition = useCallback( + ( + event: React.MouseEvent + ): { isStopExistingAtPosition: boolean; newPosition: number } => { const sliderWidth = event.currentTarget.offsetWidth; const clickedPosition = event.clientX - event.currentTarget.getBoundingClientRect().left; const newPosition = Math.ceil((clickedPosition / sliderWidth) * 100); - const isExistingPosition = positions.some( + // The 8px buffer here is the width of the thumb. We don't want to add a new stop if the user clicks on the thumb. + const isStopExistingAtPosition = positions.some( (position) => Math.abs(newPosition - position) <= 8 ); - if (isExistingPosition === true) { + return { isStopExistingAtPosition, newPosition }; + }, + [positions] + ); + + const handlePointerDown = useCallback( + (event: React.MouseEvent) => { + if (event.target === undefined || event.target === null) { + return; + } + + // radix-slider automatically brings the closest thumb to the clicked position. + // But, we want it be prevented. For adding a new color-stop where the user clicked. + // And handle the change in values only even for scrubing when the user is dragging the thumb. + const { isStopExistingAtPosition, newPosition } = + isStopExistsAtPosition(event); + if (isStopExistingAtPosition === true) { return; } @@ -131,11 +145,23 @@ export const GradientControl = (props: GradientControlProps) => { }, ...stops.slice(index), ]; + setStops(newStops); + setIsHoveredOnStop(true); + props.onChange({ + angle: props.gradient.angle, + stops: newStops, + sideOrCorner: props.gradient.sideOrCorner, + }); }, - [stops, positions] + [stops, positions, isStopExistsAtPosition, props] ); + const handleMouseEnter = (event: React.MouseEvent) => { + const { isStopExistingAtPosition } = isStopExistsAtPosition(event); + setIsHoveredOnStop(isStopExistingAtPosition); + }; + if (isEveryStopHasAPosition === false) { return; } @@ -156,15 +182,22 @@ export const GradientControl = (props: GradientControlProps) => { onValueChange={handleValueChange} onKeyDown={handleKeyDown} onPointerDown={handlePointerDown} + isHoveredOnStop={isHoveredOnStop} + onMouseEnter={handleMouseEnter} + onMouseMove={handleMouseEnter} + onMouseLeave={() => { + setIsHoveredOnStop(false); + }} > - + {stops.map((stop, index) => ( { setSelectedStop(index); + props.onThumbSelected(index, stop); }} style={{ background: toValue(stop.color), @@ -211,6 +244,16 @@ const SliderRoot = styled(Root, { borderRadius: theme.borderRadius[3], touchAction: "none", userSelect: "none", + variants: { + isHoveredOnStop: { + true: { + cursor: "default", + }, + false: { + cursor: "copy", + }, + }, + }, }); const SliderRange = styled(Range, {