Skip to content

Commit 42355aa

Browse files
committed
fix: color input validation
1 parent b5317da commit 42355aa

File tree

2 files changed

+25
-5
lines changed

2 files changed

+25
-5
lines changed

src/browser/components/ColorPicker.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useEffect, useMemo, useState } from "react";
12
import tw, { styled } from "twin.macro";
23

34
import { PRESET_COLORS } from "~/common/constants";
@@ -24,7 +25,7 @@ const Button = styled.button<ButtonProps>`
2425
`;
2526

2627
const ColorInput = styled(Input)`
27-
${tw`w-32`}
28+
${tw`font-mono w-32`}
2829
`;
2930

3031
export interface ColorPickerProps {
@@ -36,6 +37,18 @@ export interface ColorPickerProps {
3637
}
3738

3839
function ColorPicker(props: ColorPickerProps) {
40+
const [inputValue, setInputValue] = useState("");
41+
42+
const computedValue = useMemo(() => inputValue || props.value, [inputValue, props.value]);
43+
const isValid = useMemo(() => CSS.supports("color", computedValue), [computedValue]);
44+
45+
useEffect(() => setInputValue(""), [props.value]);
46+
useEffect(() => {
47+
if (isValid && inputValue.length > 0) {
48+
props.onChange(inputValue);
49+
}
50+
}, [isValid, inputValue]);
51+
3952
return (
4053
<Wrapper className={props.className} disabled={props.disabled}>
4154
{PRESET_COLORS.map((color, index) => (
@@ -48,7 +61,7 @@ function ColorPicker(props: ColorPickerProps) {
4861
/>
4962
))}
5063

51-
<ColorInput value={props.value} onChange={props.onChange} />
64+
<ColorInput error={!isValid} value={computedValue} onChange={setInputValue} />
5265
</Wrapper>
5366
);
5467
}

src/browser/components/Input.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,26 @@ const Ornament = styled.div`
55
${tw`flex-none self-center`}
66
`;
77

8-
const Wrapper = styled.fieldset`
8+
interface WrapperProps {
9+
error?: boolean;
10+
}
11+
12+
const Wrapper = styled.label<WrapperProps>`
913
${tw`flex gap-3 rounded bg-neutral-300 dark:bg-neutral-700 px-3 disabled:(cursor-default opacity-25)!`}
1014
1115
input {
12-
${tw`appearance-none bg-transparent flex-1 outline-none px-1 py-2 text-black dark:text-white`}
16+
${tw`appearance-none bg-transparent flex-1 outline-none px-1 py-2 text-current`}
1317
}
18+
19+
${(props) => props.error && tw`outline outline-2 outline-red-500 text-red-500!`}
1420
`;
1521

1622
export interface InputProps {
1723
className?: string;
1824

1925
value?: string;
2026
placeholder?: string;
27+
error?: boolean;
2128

2229
leftOrnament?: ReactNode;
2330
rightOrnament?: ReactNode;
@@ -27,7 +34,7 @@ export interface InputProps {
2734

2835
function Input(props: InputProps) {
2936
return (
30-
<Wrapper className={props.className}>
37+
<Wrapper error={props.error} className={props.className}>
3138
{props.leftOrnament && <Ornament>{props.leftOrnament}</Ornament>}
3239

3340
<input

0 commit comments

Comments
 (0)