Add ColorInput component (#455)

* Add ColorInput component

* Use valid color on input blur

* Darken input text and add labels
This commit is contained in:
Guillermo Peralta Scura 2020-01-19 18:27:36 -03:00 committed by GitHub
parent 7f6e1f420e
commit 7ae52f1164
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 96 additions and 58 deletions

View File

@ -7,6 +7,11 @@
position: relative; position: relative;
} }
.color-picker-control-container {
display: flex;
align-items: center;
}
.color-picker-triangle-shadow { .color-picker-triangle-shadow {
width: 0px; width: 0px;
height: 0px; height: 0px;
@ -33,13 +38,17 @@
padding: 15px 9px 9px 15px; padding: 15px 9px 9px 15px;
} }
.colors-gallery {
display: flex;
flex-wrap: wrap;
}
.color-picker-swatch { .color-picker-swatch {
position: relative;
height: 30px; height: 30px;
width: 30px; width: 30px;
cursor: pointer; cursor: pointer;
position: relative;
outline: none; outline: none;
float: left;
border-radius: 4px; border-radius: 4px;
margin: 0px 6px 6px 0px; margin: 0px 6px 6px 0px;
box-sizing: border-box; box-sizing: border-box;
@ -69,17 +78,20 @@
height: 30px; height: 30px;
width: 30px; width: 30px;
border-radius: 4px 0px 0px 4px; border-radius: 4px 0px 0px 4px;
float: left; color: #495057;
color: #868e96;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.color-input-container {
display: flex;
}
.color-picker-input { .color-picker-input {
width: 100px; width: 100px;
font-size: 14px; font-size: 14px;
color: #868e96; color: #343a40;
border: 0px; border: 0px;
outline: none; outline: none;
height: 28px; height: 28px;
@ -92,9 +104,8 @@
} }
.color-picker-label-swatch { .color-picker-label-swatch {
height: 24px; height: 30px;
width: 24px; width: 30px;
display: inline-block;
margin-right: 4px; margin-right: 4px;
border: 1px solid #dee2e6; border: 1px solid #dee2e6;
} }

View File

@ -15,71 +15,105 @@ const Picker = function({
color: string | undefined; color: string | undefined;
onChange: (color: string) => void; onChange: (color: string) => void;
}) { }) {
const [innerValue, setInnerValue] = React.useState(color);
React.useEffect(() => {
setInnerValue(color);
}, [color]);
return ( return (
<div className="color-picker"> <div className="color-picker">
<div className="color-picker-triangle-shadow"></div> <div className="color-picker-triangle-shadow"></div>
<div className="color-picker-triangle"></div> <div className="color-picker-triangle"></div>
<div className="color-picker-content"> <div className="color-picker-content">
{colors.map(color => ( <div className="colors-gallery">
<div {colors.map(color => (
className="color-picker-swatch" <div
onClick={() => { className="color-picker-swatch"
onChange(color); onClick={() => {
}} onChange(color);
title={color} }}
tabIndex={0} title={color}
style={{ backgroundColor: color }} tabIndex={0}
key={color} style={{ backgroundColor: color }}
> key={color}
{color === "transparent" ? ( >
<div className="color-picker-transparent"></div> {color === "transparent" ? (
) : ( <div className="color-picker-transparent"></div>
undefined ) : (
)} undefined
</div> )}
))} </div>
<div className="color-picker-hash">#</div> ))}
<div style={{ position: "relative" }}>
<input
spellCheck={false}
className="color-picker-input"
onChange={e => {
const value = e.target.value;
if (value.match(/^([0-9a-f]{3}|[0-9a-f]{6}|transparent)$/)) {
onChange(value === "transparent" ? "transparent" : "#" + value);
}
setInnerValue(value);
}}
value={(innerValue || "").replace(/^#/, "")}
/>
</div> </div>
<div style={{ clear: "both" }}></div> <ColorInput
color={color}
onChange={color => {
onChange(color);
}}
/>
</div> </div>
</div> </div>
); );
}; };
function ColorInput({
color,
onChange
}: {
color: string | undefined;
onChange: (color: string) => void;
}) {
const colorRegex = /^([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8}|transparent)$/;
const [innerValue, setInnerValue] = React.useState(color);
React.useEffect(() => {
setInnerValue(color);
}, [color]);
return (
<div className="color-input-container">
<div className="color-picker-hash">#</div>
<input
spellCheck={false}
className="color-picker-input"
aria-label="Hex color code"
onChange={e => {
const value = e.target.value;
if (value.match(colorRegex)) {
onChange(value === "transparent" ? "transparent" : "#" + value);
}
setInnerValue(value);
}}
value={(innerValue || "").replace(/^#/, "")}
onPaste={e => onChange(e.clipboardData.getData("text"))}
onBlur={() => setInnerValue(color)}
/>
</div>
);
}
export function ColorPicker({ export function ColorPicker({
type, type,
color, color,
onChange onChange
}: { }: {
type: "canvasBackground" | "elementBackground" | "elementStroke"; type: "canvasBackground" | "elementBackground" | "elementStroke";
color: string | null; color: string | undefined;
onChange: (color: string) => void; onChange: (color: string) => void;
}) { }) {
const [isActive, setActive] = React.useState(false); const [isActive, setActive] = React.useState(false);
return ( return (
<div> <div>
<button <div className="color-picker-control-container">
className="color-picker-label-swatch" <button
style={color ? { backgroundColor: color } : undefined} className="color-picker-label-swatch"
onClick={() => setActive(!isActive)} aria-label="Change color"
/> style={color ? { backgroundColor: color } : undefined}
onClick={() => setActive(!isActive)}
/>
<ColorInput
color={color}
onChange={color => {
onChange(color);
}}
/>
</div>
<React.Suspense fallback=""> <React.Suspense fallback="">
{isActive ? ( {isActive ? (
<Popover onCloseRequest={() => setActive(false)}> <Popover onCloseRequest={() => setActive(false)}>
@ -93,13 +127,6 @@ export function ColorPicker({
</Popover> </Popover>
) : null} ) : null}
</React.Suspense> </React.Suspense>
<input
type="text"
className="color-picker-swatch-input"
value={color || ""}
onPaste={e => onChange(e.clipboardData.getData("text"))}
onChange={e => onChange(e.target.value)}
/>
</div> </div>
); );
} }