Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | 1x 2x 2x 2x 1x 2x 2x 2x 2x 2x 2x 2x 2x 1x | import React, { useCallback, useMemo } from 'react';
import type * as CSS from 'csstype';
import { HsvaColor, hsvaToHslaString } from '@uiw/color-convert';
import Interactive, { type Interaction } from '@uiw/react-drag-event-interactive';
import { Pointer, type PointerProps } from './Pointer';
export interface SaturationProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
prefixCls?: string;
/** hsva => `{ h: 0, s: 75, v: 82, a: 1 }` */
hsva?: HsvaColor;
hue?: number;
radius?: string | number;
/** React Component, Custom pointer component */
pointer?: ({ prefixCls, left, top, color }: PointerProps) => JSX.Element;
onChange?: (newColor: HsvaColor) => void;
}
const Saturation = React.forwardRef<HTMLDivElement, SaturationProps>((props, ref) => {
const { prefixCls = 'w-color-saturation', radius = 0, pointer, className, hue = 0, style, hsva, onChange, ...other } = props;
const containerStyle: CSS.Properties<string | number> = {
width: 200,
height: 200,
borderRadius: radius,
...style,
position: 'relative',
};
const handleChange = (interaction: Interaction, event: MouseEvent | TouchEvent) => {
onChange &&
hsva &&
onChange({
h: hsva.h,
s: interaction.left * 100,
v: (1 - interaction.top) * 100,
a: hsva.a,
// source: 'hsv',
});
};
const handleKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (!hsva || !onChange) return;
const step = 1; // 1% step for saturation and value
let newS = hsva.s;
let newV = hsva.v;
let changed = false;
switch (event.key) {
case 'ArrowLeft':
newS = Math.max(0, hsva.s - step);
changed = true;
event.preventDefault();
break;
case 'ArrowRight':
newS = Math.min(100, hsva.s + step);
changed = true;
event.preventDefault();
break;
case 'ArrowUp':
newV = Math.min(100, hsva.v + step);
changed = true;
event.preventDefault();
break;
case 'ArrowDown':
newV = Math.max(0, hsva.v - step);
changed = true;
event.preventDefault();
break;
default:
return;
}
if (changed) {
onChange({
h: hsva.h,
s: newS,
v: newV,
a: hsva.a,
});
}
},
[hsva, onChange],
);
const pointerElement = useMemo(() => {
Iif (!hsva) return null;
const comProps = {
top: `${100 - hsva.v}%`,
left: `${hsva.s}%`,
color: hsvaToHslaString(hsva),
};
Iif (pointer && typeof pointer === 'function') {
return pointer({ prefixCls, ...comProps });
}
return <Pointer prefixCls={prefixCls} {...comProps} />;
}, [hsva, pointer, prefixCls]);
const handleClick = useCallback((event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
(event.target as HTMLElement).focus();
}, []);
return (
<Interactive
className={[prefixCls, className || ''].filter(Boolean).join(' ')}
{...other}
style={{
position: 'absolute',
inset: 0,
cursor: 'crosshair',
backgroundImage: `linear-gradient(0deg, #000, transparent), linear-gradient(90deg, #fff, hsl(${
hsva?.h ?? hue
}, 100%, 50%))`,
...containerStyle,
outline: 'none',
}}
ref={ref}
onMove={handleChange}
onDown={handleChange}
onKeyDown={handleKeyDown}
onClick={handleClick}
>
{pointerElement}
</Interactive>
);
});
Saturation.displayName = 'Saturation';
export default Saturation;
|