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 | import React, { useCallback, useMemo, useRef } from "react"; import styled from "styled-components"; type ColorSliderProps = { value: number; steps: number; colorAtValue: (value: number) => string; onChange: (value: number) => void; }; const Wrapper = styled.div` display: flex; position: relative; `; const Color = styled.div` flex-grow: 1; max-width: 32px; height: 32px; border: 1px solid transparent; &:first-of-type { border-top-left-radius: 4px; border-bottom-left-radius: 4px; min-width: 5px; flex-shrink: 0; } &:nth-last-of-type(2) { border-top-right-radius: 4px; border-bottom-right-radius: 4px; min-width: 5px; flex-shrink: 0; } `; const Handle = styled.div` position: absolute; width: 10px; margin-left: -5px; margin-top: -3px; height: 100%; border-radius: 4px; padding: 2px; box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.3); border: 1px solid ${(props) => props.theme.colors.input.background}; `; const ColorSlider = ({ value, onChange, steps, colorAtValue, }: ColorSliderProps) => { const boundingRect = useRef<DOMRect>(); const stepValues = useMemo(() => Array.from(Array(steps).keys()), [steps]); const onMouseMove = useCallback( (e: MouseEvent) => { Iif (boundingRect.current) { const x = e.clientX - boundingRect.current.left; const value = x / boundingRect.current.width; onChange(value); } }, [onChange] ); const onMouseUp = useCallback( (_e: MouseEvent) => { window.removeEventListener("mousemove", onMouseMove); window.removeEventListener("mouseup", onMouseUp); }, [onMouseMove] ); const onMouseDown = useCallback( (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => { Iif (!(e.currentTarget instanceof HTMLElement)) { return; } boundingRect.current = e.currentTarget.getBoundingClientRect(); const x = e.clientX - boundingRect.current.left; const value = x / boundingRect.current.width; onChange(value); window.addEventListener("mousemove", onMouseMove); window.addEventListener("mouseup", onMouseUp); }, [onChange, onMouseMove, onMouseUp] ); return ( <Wrapper onMouseDown={onMouseDown}> {stepValues.map((stepIndex) => { const normalisedValue = stepIndex / (stepValues.length - 1); const color = colorAtValue(normalisedValue); return ( <Color key={stepIndex} style={{ backgroundColor: color, }} /> ); })} <Handle style={{ left: `${value * 100}%`, backgroundColor: colorAtValue(value), }} /> </Wrapper> ); }; export default ColorSlider; |