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

fix(@clayui/color-picker): Update hue slider to use html range input #5156

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/clay-color-picker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@clayui/form": "^3.79.0",
"@clayui/icon": "^3.56.0",
"@clayui/shared": "^3.79.0",
"@clayui/slider": "^3.78.1",
"classnames": "^2.2.6",
"tinycolor2": "^1.4.2"
},
Expand Down
107 changes: 60 additions & 47 deletions packages/clay-color-picker/src/Hue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
* SPDX-License-Identifier: BSD-3-Clause
*/

import React from 'react';

import {usePointerPosition} from './hooks';
import {hueToX, xToHue} from './util';
import {ClayInput} from '@clayui/form';
import ClaySlider from '@clayui/slider';
import React, {useState} from 'react';

type Props = {
/**
Expand All @@ -20,65 +19,79 @@ type Props = {
value: number;
};

const useIsomorphicLayoutEffect =
typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;

/**
* Renders Hue component
*/
const ClayColorPickerHue = ({value = 0, onChange = () => {}}: Props) => {
const containerRef = React.useRef<HTMLDivElement>(null);
const selectorActive = React.useRef<boolean>(false);
const [internalValue, setInternalValue] = useState<number>(value);
Copy link
Member

Choose a reason for hiding this comment

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

We don't need another state here, because we also have a state in the parent component, in which case we have access to them which are value and onChange it is easier to maintain a source of truth than to have multiple states and keep synchronizing them all, this is also bad because of unnecessary rerenders.


const {onPointerMove, setXY, x, y} = usePointerPosition(containerRef);
const handleOnChangeEnd = (event: any) => {
Copy link
Member

Choose a reason for hiding this comment

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

Since we won't have the internal state anymore, just add the onChange callback as a property of the ClaySlider, we won't need that anymore because we will be directly updating the parent state instead of the internal one.

onChange(internalValue);

const removeListeners = () => {
selectorActive.current = false;
event.target.blur();

window.removeEventListener('pointermove', onPointerMove);
window.removeEventListener('pointerup', removeListeners);
event.target.focus();
};

useIsomorphicLayoutEffect(() => {
if (containerRef.current && selectorActive.current) {
onChange(xToHue(x, containerRef.current));
}
}, [x]);

React.useEffect(() => {
if (containerRef.current) {
setXY({x: hueToX(value, containerRef.current), y});
}
setInternalValue(value);
Comment on lines 36 to +37
Copy link
Member

Choose a reason for hiding this comment

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

As we are going to use the parent state value and onChange we no longer need this to synchronize the values.

Copy link
Member Author

Choose a reason for hiding this comment

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

I couldn't do this one due to #5156 (comment).

}, [value]);

React.useEffect(() => removeListeners, []);

return (
<div
className="clay-color-range clay-color-range-hue"
onPointerDown={(event) => {
event.preventDefault();

selectorActive.current = true;
onPointerMove(event);

(containerRef.current!.querySelector(
'.clay-color-range-pointer'
) as HTMLElement)!.focus();

window.addEventListener('pointermove', onPointerMove);
window.addEventListener('pointerup', removeListeners);
}}
ref={containerRef}
>
<button
className="clay-color-pointer clay-color-range-pointer"
<div className="clay-color-form-group">
<ClaySlider
className="clay-color-slider clay-color-slider-hue"
max={360}
min={0}
onChange={setInternalValue}
onKeyUp={(event) => {
const arrowKeys = [
'ArrowDown',
'ArrowLeft',
'ArrowRight',
'ArrowUp',
];

if (arrowKeys.indexOf(event.key) > -1) {
handleOnChangeEnd(event);
}
}}
onPointerUp={handleOnChangeEnd}
showTooltip={false}
step={1}
style={{
background: `hsl(${value}, 100%, 50%)`,
left: x - 7,
color: `hsl(${internalValue}, 100%, 50%)`,
}}
type="button"
value={internalValue}
/>
<ClayInput.Group>
<ClayInput.GroupItem>
<ClayInput
data-testid="hInput"
insetBefore
max="360"
min="0"
onChange={(event) => {
const value = event.target.value;

let newVal = Number(value);

if (newVal < 0) {
newVal = 0;
} else if (newVal > 360) {
newVal = 360;
}

onChange(newVal);
}}
type="number"
value={value}
/>
<ClayInput.GroupInsetItem before tag="label">
H
</ClayInput.GroupInsetItem>
</ClayInput.GroupItem>
</ClayInput.Group>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,57 @@ exports[`Interactions color editor interactions ability to change color of click
</div>
</div>
<div
class="clay-color-range clay-color-range-hue"
class="clay-color-form-group"
>
<button
class="clay-color-pointer clay-color-range-pointer"
style="background: rgb(255, 0, 0); left: -7px;"
type="button"
/>
<div
class="clay-range clay-color-slider clay-color-slider-hue"
>
<div
class="clay-range-input"
>
<input
class="form-control-range"
max="360"
min="0"
step="1"
style="color: rgb(255, 0, 0);"
type="range"
value="0"
/>
<div
class="clay-range-track"
/>
<div
class="clay-range-progress"
style="width: 0%;"
>
<div
class="clay-range-thumb"
/>
</div>
</div>
</div>
<div
class="input-group"
>
<div
class="input-group-item"
>
<input
class="form-control input-group-inset input-group-inset-before"
data-testid="hInput"
max="360"
min="0"
type="number"
value="0"
/>
<label
class="input-group-inset-item input-group-inset-item-before"
>
H
</label>
</div>
</div>
</div>
<div
class="clay-color-footer"
Expand Down
17 changes: 6 additions & 11 deletions packages/clay-color-picker/src/__tests__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,22 +335,17 @@ describe('Interactions', () => {
});

it('changes the color by changing the hue', () => {
const hueSelector = document.querySelector('.clay-color-range-hue');
const hueSelector = document.querySelector(
'.clay-color-slider-hue .form-control-range'
);

mockClientRect(hueSelector as HTMLElement);

const mouseDown = getMouseEvent('pointerdown', {
pageX: 0,
pageY: 0,
});
fireEvent.change(hueSelector as HTMLElement, {target: {value: 50}});

fireEvent(hueSelector as HTMLElement, mouseDown);

const mouseMove = getMouseEvent('pointermove', {
pageX: 50,
});
const mouseUp = getMouseEvent('pointerup', {});

fireEvent(hueSelector as HTMLElement, mouseMove);
fireEvent(hueSelector as HTMLElement, mouseUp);

expect(handleColorsChange).toBeCalledTimes(1);
});
Expand Down
42 changes: 42 additions & 0 deletions packages/clay-css/src/scss/cadmin/components/_clay-color.scss
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,48 @@
@include clay-button-variant($cadmin-clay-color-range-pointer);
}

// Clay Color Slider

.clay-color-slider {
@include clay-range-variant($cadmin-clay-color-slider);
}

.clay-color-slider-hue {
@include clay-range-variant($cadmin-clay-color-slider-hue);
}

.clay-color-slider-alpha {
@include clay-range-variant($cadmin-clay-color-slider-alpha);
}

// Clay Color Form Group

.clay-color-form-group {
@include clay-css($cadmin-clay-color-form-group);

.clay-range {
@include clay-css(map-get($cadmin-clay-color-form-group, clay-range));
}

.form-control {
@include clay-form-control-variant(
map-get($cadmin-clay-color-form-group, form-control)
);
}

.input-group {
$input-group: map-get($cadmin-clay-color-form-group, input-group);

@include clay-input-group-variant($cadmin-input-group);

.input-group-inset-item-before {
@include clay-css(
map-get($input-group, input-group-inset-item-before)
);
}
}
}

// Clay Color Sm

%clay-color-sm-input-group-inset-item-before {
Expand Down