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; |