Files
outline/shared/components/ColorPicker.tsx
T
dependabot[bot] 620c654b26 chore(deps): bump react-colorful from 5.6.1 to 5.7.0 (#12386)
* chore(deps): bump react-colorful from 5.6.1 to 5.7.0

Bumps [react-colorful](https://github.com/omgovich/react-colorful) from 5.6.1 to 5.7.0.
- [Release notes](https://github.com/omgovich/react-colorful/releases)
- [Changelog](https://github.com/omgovich/react-colorful/blob/master/CHANGELOG.md)
- [Commits](https://github.com/omgovich/react-colorful/commits/5.7.0)

---
updated-dependencies:
- dependency-name: react-colorful
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Use new prop

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
2026-05-18 22:57:52 -04:00

176 lines
3.9 KiB
TypeScript

import copy from "copy-to-clipboard";
import { CheckmarkIcon, CopyIcon } from "outline-icons";
import { useState, useRef, useCallback } 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 theme = useTheme();
const wrapperRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
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;