Skip to content
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

feat(react-color-picker): Added transparent option to the AlphaSlider #33572

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
ValentinaKozlova marked this conversation as resolved.
Show resolved Hide resolved
"type": "patch",
"comment": "feat: Added `transparent` option to the AlphaSlider",
"packageName": "@fluentui/react-color-picker-preview",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export const AlphaSlider: ForwardRefComponent<AlphaSliderProps>;
export const alphaSliderClassNames: SlotClassNames<AlphaSliderSlots>;

// @public
export type AlphaSliderProps = ColorSliderProps;
export type AlphaSliderProps = ColorSliderProps & {
transparency?: boolean;
};

// @public (undocumented)
export type AlphaSliderSlots = ColorSliderSlots;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ export type AlphaSliderSlots = ColorSliderSlots;
/**
* AlphaSlider Props
*/
export type AlphaSliderProps = ColorSliderProps;
export type AlphaSliderProps = ColorSliderProps & {
/**
* The `transparency` property determines how the alpha channel is interpreted.
* - When `false`, the alpha channel represents the opacity of the color.
* - When `true`, the alpha channel represents the transparency of the color.
* For example, a 30% transparent color has 70% opacity.
*
* @defaultvalue false
*/
transparency?: boolean;
ValentinaKozlova marked this conversation as resolved.
Show resolved Hide resolved
};

/**
* State used in rendering AlphaSlider
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { adjustToTransparency, calculateTransparencyValue, getSliderDirection } from './alphaSliderUtils';

describe('AlphaSlider Utils', () => {
describe('adjustToTransparency', () => {
it('should return 100 - value when transparency is true', () => {
expect(adjustToTransparency(30, true)).toBe(70);
});

it('should return value when transparency is false', () => {
expect(adjustToTransparency(30, false)).toBe(30);
});
});

describe('calculateTransparencyValue', () => {
it('should return adjusted value when value is provided and transparency is true', () => {
expect(calculateTransparencyValue(true, 0.3)).toBe(70);
});

it('should return adjusted value when value is provided and transparency is false', () => {
expect(calculateTransparencyValue(false, 0.3)).toBe(30);
});

it('should return undefined when value is not provided', () => {
expect(calculateTransparencyValue(true)).toBeUndefined();
expect(calculateTransparencyValue(false)).toBeUndefined();
});
});

describe('getSliderDirection', () => {
it('should return "180deg" when vertical is true and transparency is true', () => {
expect(getSliderDirection('ltr', true, true)).toBe('180deg');
});

it('should return "0deg" when vertical is true and transparency is false', () => {
expect(getSliderDirection('ltr', true, false)).toBe('0deg');
});

it('should return "90deg" when dir is "ltr" and transparency is false', () => {
expect(getSliderDirection('ltr', false, false)).toBe('90deg');
});

it('should return "-90deg" when dir is "ltr" and transparency is true', () => {
expect(getSliderDirection('ltr', false, true)).toBe('-90deg');
});

it('should return "-90deg" when dir is "rtl" and transparency is false', () => {
expect(getSliderDirection('rtl', false, false)).toBe('-90deg');
});

it('should return "-90deg" when dir is "rtl" and transparency is true', () => {
expect(getSliderDirection('rtl', false, true)).toBe('-90deg');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Adjusts the given value based on the transparency flag.
*
* @param value - The numeric value to adjust.
* @param transparency - A boolean flag indicating whether to adjust for transparency.
* @returns The adjusted value.
*/
export function adjustToTransparency(value: number, transparency: boolean) {
return transparency ? 100 - value : value;
}

/**
* Calculates the transparency value based on the given parameters.
*
* @param transparency - A boolean flag indicating whether to adjust for transparency.
* @param value - An optional numeric value to adjust.
* @returns The calculated transparency value or undefined if the value is not provided.
*/
export function calculateTransparencyValue(transparency: boolean, value?: number) {
return value !== undefined ? adjustToTransparency(value * 100, transparency) : undefined;
}

/**
* Determines the direction of the slider based on the given parameters.
*
* @param dir - The text direction, either 'ltr' (left-to-right) or 'rtl' (right-to-left).
* @param vertical - A boolean indicating if the slider is vertical.
* @param transparency - A boolean indicating if the slider is for transparency.
* @returns The direction of the slider as a string representing degrees (e.g., '90deg').
*/
export function getSliderDirection(dir: 'ltr' | 'rtl', vertical: boolean, transparency: boolean) {
if (vertical) {
return transparency ? '180deg' : '0deg';
}
return dir === 'ltr' && !transparency ? '90deg' : '-90deg';
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,41 @@ import { useColorPickerContextValue_unstable } from '../../contexts/colorPicker'
import { MIN, MAX } from '../../utils/constants';
import { getPercent } from '../../utils/getPercent';
import type { HsvColor } from '../../types/color';
import { adjustToTransparency, calculateTransparencyValue, getSliderDirection } from './alphaSliderUtils';

export const useAlphaSliderState_unstable = (state: AlphaSliderState, props: AlphaSliderProps) => {
'use no memo';

const { dir } = useFluent();
const onChangeFromContext = useColorPickerContextValue_unstable(ctx => ctx.requestChange);
const colorFromContext = useColorPickerContextValue_unstable(ctx => ctx.color);
const { color, onChange = onChangeFromContext } = props;
const { color, onChange = onChangeFromContext, transparency = false, vertical = false } = props;
const hsvColor = color || colorFromContext;
const hslColor = tinycolor(hsvColor).toHsl();

const [currentValue, setCurrentValue] = useControllableState({
defaultState: props.defaultColor?.a ? props.defaultColor.a * 100 : undefined,
state: hsvColor?.a ? hsvColor.a * 100 : undefined,
initialState: 100,
defaultState: calculateTransparencyValue(transparency, props.defaultColor?.a),
state: calculateTransparencyValue(transparency, hsvColor?.a),
initialState: adjustToTransparency(100, transparency),
});

const clampedValue = clamp(currentValue, MIN, MAX);
const valuePercent = getPercent(clampedValue, MIN, MAX);

const inputOnChange = state.input.onChange;

const _onChange: React.ChangeEventHandler<HTMLInputElement> = useEventCallback(event => {
const newValue = Number(event.target.value);
const newValue = adjustToTransparency(Number(event.target.value), transparency);
const newColor: HsvColor = { ...hsvColor, a: newValue / 100 };
setCurrentValue(newValue);
inputOnChange?.(event);
onChange?.(event, { type: 'change', event, color: newColor });
});

const sliderDirection = getSliderDirection(dir, vertical, transparency);

const rootVariables = {
[alphaSliderCSSVars.sliderDirectionVar]: state.vertical ? '0deg' : dir === 'ltr' ? '90deg' : '-90deg',
[alphaSliderCSSVars.sliderDirectionVar]: sliderDirection,
[alphaSliderCSSVars.sliderProgressVar]: `${valuePercent}%`,
[alphaSliderCSSVars.thumbColorVar]: `transparent`,
[alphaSliderCSSVars.railColorVar]: `hsl(${hslColor.h} ${hslColor.s * 100}%, ${hslColor.l * 100}%)`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export type ColorSliderProps = Omit<
vertical?: boolean;

/**
* Color of the COlorPicker
* Color of the ColorPicker
*/
color?: HsvColor;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ export const AlphaSliderExample = (props: Partial<AlphaSliderProps>) => {
const styles = useStyles();

const [color, setColor] = React.useState(COLOR);
const [transparancyColor, setTransparancyColor] = React.useState(COLOR);
const [value, setValue] = React.useState(COLOR.a * 100);
const onSliderChange: AlphaSliderProps['onChange'] = (_, data) => {
const alpha = data.color.a ?? 1;
setColor({ ...data.color, a: alpha });
setValue(alpha * 100);
};
const onTransparancySliderChange: AlphaSliderProps['onChange'] = (_, data) =>
setTransparancyColor({ ...data.color, a: data.color.a ?? 1 });
const resetSlider = () => setColor(COLOR);
const resetTransparencySlider = () => setTransparancyColor(COLOR);

return (
<div className={styles.example}>
Expand All @@ -48,6 +52,26 @@ export const AlphaSliderExample = (props: Partial<AlphaSliderProps>) => {
/>
<div className={styles.previewColor} style={{ backgroundColor: tinycolor(color).toRgbString() }} />
<Button onClick={resetSlider}>Reset</Button>
<h3>Transparency</h3>
<AlphaSlider
color={transparancyColor}
onChange={onTransparancySliderChange}
aria-valuetext={`${value}%`}
aria-label="Alpha"
transparency
{...props}
/>
<AlphaSlider
color={transparancyColor}
onChange={onTransparancySliderChange}
aria-valuetext={`${value}%`}
aria-label="Vertical alpha"
transparency
vertical
{...props}
/>
<div className={styles.previewColor} style={{ backgroundColor: tinycolor(transparancyColor).toRgbString() }} />
<Button onClick={resetTransparencySlider}>Reset</Button>
</div>
);
};
Expand Down
Loading