shared/components/ColorPicker.tsx
Rama main · réplica de lectura del repositorio original.
import copy from "copy-to-clipboard";
import { CheckmarkIcon, CopyIcon } from "outline-icons";
import { useState, useRef, useCallback, useEffect } from "react";
import {
HexColorInput,
HexAlphaColorPicker,
HexColorPicker,
} from "react-colorful";
import styled, { css, useTheme } from "styled-components";
import { s } from "../styles";
import { darken } from "polished";
type Props = {
onSelect: (color: string) => void;
/** The currently active color */
activeColor?: string | null;
alpha: boolean;
};
const DEFAULT_COLOR = "#7e3d3db3";
function ColorPicker({ activeColor, onSelect, alpha }: Props) {
const [color, setColor] = useState(activeColor || DEFAULT_COLOR);
const [copied, setCopied] = useState(false);
const wrapperRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const theme = useTheme();
// Keep the internal color in sync when the active color is changed externally
useEffect(() => {
setColor(activeColor || DEFAULT_COLOR);
}, [activeColor]);
const applyColor = useCallback(
(newColor: string) => {
const activeElement = document.activeElement as HTMLElement | null;
const hadFocusInside = wrapperRef.current?.contains(activeElement);
onSelect(newColor);
if (hadFocusInside && activeElement) {
activeElement.focus();
}
},
[onSelect]
);
const handleColorChangeInput = (newColor: string) => {
setColor(newColor);
applyColor(newColor);
};
const handleCopy = useCallback(() => {
copy(color);
buttonRef.current?.focus();
setCopied(true);
setTimeout(() => setCopied(false), 500);
}, [color]);
return (
<Wrapper ref={wrapperRef} tabIndex={-1}>
{alpha ? (
<StyledHexAlphaColorPicker
color={color}
onChange={setColor}
onChangeEnd={applyColor}
/>
) : (
<StyledHexNonAlphaColorPicker
color={color}
onChange={setColor}
onChangeEnd={applyColor}
/>
)}
<InputRow>
<StyledHexColorInput
color={color}
onChange={handleColorChangeInput}
prefixed
alpha={alpha}
/>
<CopyButton ref={buttonRef} onClick={handleCopy} type="button">
{copied ? (
<CheckmarkIcon size={16} color={darken(0.2, theme.brand.green)} />
) : (
<CopyIcon size={16} />
)}
</CopyButton>
</InputRow>
</Wrapper>
);
}
const Wrapper = styled.div`
padding: 4px;
display: flex;
flex-direction: column;
`;
const colorPickerStyles = css`
&.react-colorful {
width: auto;
& > .react-colorful__saturation {
border-radius: 4px 4px 0 0;
}
& .react-colorful__pointer {
width: 14px;
height: 14px;
}
& .react-colorful__interactive:focus .react-colorful__pointer {
transform: translate(-50%, -50%) scale(1.25);
}
& > .react-colorful__hue {
height: 8px;
border-radius: 0 0 4px 4px;
margin-bottom: 8px;
}
}
`;
const StyledHexNonAlphaColorPicker = styled(HexColorPicker)`
${colorPickerStyles}
`;
const StyledHexAlphaColorPicker = styled(HexAlphaColorPicker)`
${colorPickerStyles}
&.react-colorful > .react-colorful__alpha {
height: 8px;
border-radius: 4px;
margin-top: 8px;
margin-bottom: 8px;
}
`;
const InputRow = styled.div`
display: flex;
justify-content: space-between;
gap: 4px;
margin-top: 8px;
`;
const StyledHexColorInput = styled(HexColorInput)`
flex: 1;
padding: 4px 6px;
border: 1px solid ${s("inputBorder")};
border-radius: 4px;
font-size: 12px;
background: ${s("background")};
color: ${s("text")};
&:focus {
outline: none;
border-color: ${s("accent")};
}
`;
const CopyButton = styled.button`
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
border: 1px solid ${s("inputBorder")};
border-radius: 4px;
background: ${s("background")};
color: ${s("textSecondary")};
cursor: pointer;
&:hover {
background: ${s("backgroundSecondary")};
color: ${s("text")};
}
`;
export default ColorPicker;