Skip to content

ColorSliderGroup - refactor #3009

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions demo/src/screens/incubatorScreens/IncubatorSliderScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const MAX = 350;
const INITIAL_MIN = 30;
const INITIAL_MAX = 270;
const COLOR = Colors.blue30;
const GROUP_COLOR = Colors.yellow30;

const IncubatorSliderScreen = () => {
const [disableRTL, setDisableRTL] = useState<boolean>(false);
Expand All @@ -21,7 +22,7 @@ const IncubatorSliderScreen = () => {
const [sliderMaxValue, setSliderMaxValue] = useState(INITIAL_MAX);

const [color, setColor] = useState(COLOR);
const [groupColor, setGroupColor] = useState(Colors.yellow30);
const [groupColor, setGroupColor] = useState(GROUP_COLOR);
const [alpha, setAlpha] = useState(1);

const slider = useRef<Incubator.SliderRef>(null);
Expand Down Expand Up @@ -236,7 +237,7 @@ const IncubatorSliderScreen = () => {
Color Slider Group
</Text>
<ColorSliderGroup
initialColor={groupColor}
initialColor={GROUP_COLOR}
sliderContainerStyle={styles.slider}
containerStyle={[styles.group, {borderWidth: 12, borderColor: groupColor}]}
showLabels
Expand Down
43 changes: 0 additions & 43 deletions src/components/slider/ColorSlider.tsx

This file was deleted.

73 changes: 55 additions & 18 deletions src/components/slider/ColorSliderGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import _ from 'lodash';
import React, {useState, useEffect, useCallback, useMemo} from 'react';
import React, {useState, useCallback, useMemo} from 'react';
import {Colors} from '../../style';
import {useThemeProps} from '../../hooks';
import View from '../view';
import Text from '../text';
import {ColorSliderGroupProps, HSLA, GradientSliderTypes} from './types';
import SliderContext from './SliderContext';
import GradientSlider from './GradientSlider';
import SliderGroup from './context/SliderGroup';
import ColorSlider from './ColorSlider';
import {ColorSliderGroupProps, HSLA} from './types';

/**
* @description: A Gradient Slider component
Expand All @@ -14,27 +15,63 @@ import {ColorSliderGroupProps, HSLA} from './types';
*/
const ColorSliderGroup = <T extends string | HSLA = string>(props: ColorSliderGroupProps<T>) => {
const themeProps = useThemeProps(props, 'ColorSliderGroup');
const {initialColor, containerStyle, onValueChange, ...others} = themeProps;
const {
initialColor,
onValueChange,
containerStyle,
sliderContainerStyle,
showLabels,
labels = {hue: 'Hue', lightness: 'Lightness', saturation: 'Saturation', default: ''},
labelsStyle,
accessible,
migrate
} = themeProps;

const _initialColor = useMemo<HSLA>(() => {
return _.isString(initialColor) ? Colors.getHSL(initialColor) : initialColor;
}, [initialColor]);
const [color, setColor] = useState<HSLA>(_initialColor);

useEffect(() => {
setColor(_initialColor);
}, [_initialColor]);

const _onValueChange = useCallback((value: HSLA) => {
const _value = _.isString(initialColor) ? Colors.getHexString(value) : value;
onValueChange?.(_value as T);
}, [onValueChange, initialColor]);
const newValue = _.isString(initialColor) ? Colors.getHexString(value) : value;
onValueChange?.(newValue as T);
}, [initialColor, onValueChange]);

const [value, setValue] = useState(_initialColor);

const _setValue = useCallback((value: HSLA) => {
setValue(value);
_onValueChange?.(value);
}, [_onValueChange]);

const contextProviderValue = useMemo(() => ({value, setValue: _setValue}), [value, _setValue]);

const renderSlider = (type: GradientSliderTypes) => {
return (
<>
{showLabels && labels && (
<Text recorderTag={'unmask'} $textNeutral text80 style={labelsStyle} accessible={accessible}>
{labels[type]}
</Text>
)}
<GradientSlider
color={value}
type={type}
containerStyle={sliderContainerStyle}
accessible={accessible}
migrate={migrate}
/>
</>
);
};

return (
<SliderGroup style={containerStyle} color={color} onValueChange={_onValueChange}>
<ColorSlider type={GradientSlider.types.HUE} initialColor={_initialColor} {...others}/>
<ColorSlider type={GradientSlider.types.SATURATION} initialColor={_initialColor} {...others}/>
<ColorSlider type={GradientSlider.types.LIGHTNESS} initialColor={_initialColor} {...others}/>
</SliderGroup>
<View style={containerStyle}>
<SliderContext.Provider value={contextProviderValue}>
{renderSlider(GradientSlider.types.HUE)}
{renderSlider(GradientSlider.types.SATURATION)}
{renderSlider(GradientSlider.types.LIGHTNESS)}
</SliderContext.Provider>
</View>
);
};

Expand Down
46 changes: 15 additions & 31 deletions src/components/slider/GradientSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import _ from 'lodash';
import React, {useCallback, useEffect, useState, useMemo} from 'react';
import React, {useCallback, useMemo, useContext} from 'react';
import {asBaseComponent, forwardRef, ForwardRefInjectedProps} from '../../commons/new';
import {ComponentStatics} from '../../typings/common';
import {Colors} from '../../style';
import {Slider as NewSlider} from '../../incubator';
import Gradient from '../gradient';
import {GradientSliderProps, GradientSliderTypes, HSLA} from './types';
import Slider from './index';
import {SliderContextProps} from './context/SliderContext';
import asSliderGroupChild from './context/asSliderGroupChild';
import SliderContext from './SliderContext';

type GradientSliderComponentProps<T> = {
sliderContext: SliderContextProps;
} & GradientSliderProps<T>;

type Props<T> = GradientSliderComponentProps<T> & ForwardRefInjectedProps;
type Props<T> = GradientSliderProps<T> & ForwardRefInjectedProps;

/**
* @description: A Gradient Slider component
Expand All @@ -25,29 +21,25 @@ const GradientSlider = <T extends string | HSLA = string>(props: Props<T>) => {
const {
type = GradientSliderTypes.DEFAULT,
gradientSteps = 120,
color: propsColors = Colors.$backgroundPrimaryHeavy,
sliderContext,
onValueChange: _onValueChange,
color: propsColor = Colors.$backgroundPrimaryHeavy, // initialColor
onValueChange,
migrate,
containerStyle,
disabled,
accessible,
forwardedRef,
...others
} = props;
const sliderContext = useContext(SliderContext);


const initialColor = useMemo((): HSLA => {
return _.isString(propsColors) ? Colors.getHSL(propsColors) : propsColors;
}, [propsColors]);
const [color, setColor] = useState(initialColor);

useEffect(() => {
setColor(initialColor);
}, [initialColor]);
return _.isString(propsColor) ? Colors.getHSL(propsColor) : propsColor;
}, [propsColor]);

const getColor = useCallback(() => {
return color || sliderContext.value;
}, [color, sliderContext.value]);
return initialColor || sliderContext.value;
}, [initialColor, sliderContext.value]);

const hueColor = useMemo(() => {
const color = getColor();
Expand Down Expand Up @@ -75,19 +67,12 @@ const GradientSlider = <T extends string | HSLA = string>(props: Props<T>) => {
return <Gradient type={Gradient.types.SATURATION} color={hueColor} numberOfSteps={gradientSteps}/>;
}, [hueColor, gradientSteps]);

const onValueChange = useCallback((value: string, alpha: number) => {
// alpha returns for type.DEFAULT
_onValueChange?.(value, alpha);
},
[_onValueChange]);

const updateColor = useCallback((color: HSLA) => {
if (!_.isEmpty(sliderContext)) {
sliderContext.setValue?.(color);
} else {
setColor(color);
const hex = Colors.getHexString(color);
onValueChange(hex, color.a);
const hex = Colors.getHexString(color); // alpha returns for type.DEFAULT
onValueChange?.(hex, color.a);
}
},
[sliderContext, onValueChange]);
Expand Down Expand Up @@ -122,7 +107,7 @@ const GradientSlider = <T extends string | HSLA = string>(props: Props<T>) => {

let step = 0.01;
let maximumValue = 1;
let value = color.a;
let value = initialColor.a;
let renderTrack = renderDefaultGradient;
let sliderOnValueChange = updateAlpha;

Expand Down Expand Up @@ -172,6 +157,5 @@ GradientSlider.displayName = 'GradientSlider';
GradientSlider.types = GradientSliderTypes;
// @ts-expect-error
export default asBaseComponent<GradientSliderProps, ComponentStatics<typeof GradientSlider>>(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add another simplification
Replace asBaseComponent HOC with the useThemeProps (see usages in View component)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got issues with the statics and the forwardRef so I decided to leave it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works for me or at least It didn't throw any errors...

export default forwardRef<GradientSliderProps<string | HSLA>, ComponentStatics<typeof GradientSlider>>(GradientSlider);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I didn't move the forwardRef... I'll make the change

// @ts-expect-error
forwardRef(asSliderGroupChild(forwardRef(GradientSlider)))
forwardRef(forwardRef(GradientSlider))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have forwardRef twice

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

);
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import {HSLA} from '../types';
import {HSLA} from './types';

export interface SliderContextProps {
export interface SliderContextType {
value?: HSLA;
setValue?: (value: HSLA) => void;
}

const SliderContext: React.Context<SliderContextProps> = React.createContext({});
const SliderContext: React.Context<SliderContextType> = React.createContext({});
SliderContext.displayName = 'IGNORE';
export default SliderContext;
34 changes: 0 additions & 34 deletions src/components/slider/context/SliderGroup.tsx

This file was deleted.

37 changes: 0 additions & 37 deletions src/components/slider/context/asSliderGroupChild.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/incubator/Slider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ const Slider = React.memo((props: Props) => {
}, [defaultThumbStyle, thumbStyle]);

const onValueChangeThrottled = useCallback(_.throttle(value => {
if (!didValueUpdate.current) { // NOTE: fix for GradientSlider (should be removed after fix in the GradientSlider component): don't invoke onChange when slider's value changes to prevent updates loop
if (!didValueUpdate.current) { // NOTE: don't invoke onValueChange when slider's value prop updated programmatically
onValueChange?.(value);
} else {
didValueUpdate.current = false;
Expand Down