增加HSL输入功能。

This commit is contained in:
徐涛 2024-12-31 15:20:33 +08:00
parent 67f220facb
commit cac57726ff
2 changed files with 255 additions and 0 deletions

View File

@ -0,0 +1,21 @@
@layer components {
.rgb_input {
display: flex;
flex-direction: column;
align-items: stretch;
gap: var(--spacing-m);
.rgb_input {
text-align: right;
text-transform: uppercase;
}
.component_row {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--spacing-xs);
.component_input {
flex: 1 0;
}
}
}
}

View File

@ -0,0 +1,234 @@
import { Icon } from '@iconify/react/dist/iconify.js';
import cx from 'clsx';
import { clamp, divide, floor, times, trim } from 'lodash-es';
import { ChangeEvent, useEffect, useState } from 'react';
import { useColorFunction } from '../ColorFunctionContext';
import styles from './ColorComponentInput.module.css';
type ColorComponentInputProps = {
color: string | null;
onChange: (color: string) => void;
};
export function ColorComponentInput({ color, onChange }: ColorComponentInputProps) {
const { colorFn } = useColorFunction();
const [hex, setHex] = useState<string>(color ?? '000000');
const [r, setR] = useState<number>(0);
const [g, setG] = useState<number>(0);
const [b, setB] = useState<number>(0);
const [h, setH] = useState<number>(0);
const [s, setS] = useState<number>(0);
const [l, setL] = useState<number>(0);
const updateHex = (evt: ChangeEvent<HTMLInputElement>) => {
try {
const hexValue = trim(evt.target.value, '#');
const [r, g, b] = colorFn?.represent_rgb(hexValue) ?? [0, 0, 0];
setR(r);
setG(g);
setB(b);
const [h, s, l] = colorFn?.represent_hsl(hexValue) ?? [0, 0, 0];
setH(h);
setS(s);
setL(l);
setHex(hexValue);
onChange(hexValue);
} catch (e) {
console.error('[Convert HEX]', e);
}
};
const updateRGB = (colorValue: string) => {
try {
const [r, g, b] = colorFn?.represent_rgb(colorValue) ?? [0, 0, 0];
setR(r);
setG(g);
setB(b);
} catch (e) {
console.error('[Convert RGB]', e);
}
};
const updateHSL = (colorValue: string) => {
try {
const [h, s, l] = colorFn?.represent_hsl(colorValue) ?? [0, 0, 0];
setH(floor(h, 1));
setS(floor(times(s, 100), 2));
setL(floor(times(l, 100), 2));
} catch (e) {
console.error('[Convert HSL]', e);
}
};
const updateR = (evt: ChangeEvent<HTMLInputElement>) => {
const value = clamp(parseInt(evt.target.value, 10), 0, 255);
try {
const cHex = colorFn?.rgb_to_hex(value, g, b);
setR(value);
setHex(cHex);
updateHSL(cHex);
onChange(cHex);
} catch (e) {
console.error('[Update R]', e, value);
}
};
const updateG = (evt: ChangeEvent<HTMLInputElement>) => {
const value = clamp(parseInt(evt.target.value, 10), 0, 255);
try {
const cHex = colorFn?.rgb_to_hex(r, value, b);
setG(value);
setHex(cHex);
updateHSL(cHex);
onChange(cHex);
} catch (e) {
console.error('[Update G]', e, value);
}
};
const updateB = (evt: ChangeEvent<HTMLInputElement>) => {
const value = clamp(parseInt(evt.target.value, 10), 0, 255);
try {
const cHex = colorFn?.rgb_to_hex(r, g, value);
setB(value);
setHex(cHex);
updateHSL(cHex);
onChange(cHex);
} catch (e) {
console.error('[Update B]', e, value);
}
};
const updateH = (evt: ChangeEvent<HTMLInputElement>) => {
const value = parseInt(evt.target.value, 10);
if (value > 360) {
value -= 360;
}
if (value < 0) {
value += 360;
}
try {
const cHex = colorFn.hsl_to_hex(value, divide(s, 100), divide(l, 100));
setH(value);
setHex(cHex);
updateRGB(cHex);
onChange(cHex);
} catch (e) {
console.error('[Update Hue]', e, value);
}
};
const updateS = (evt: ChangeEvent<HTMLInputElement>) => {
const value = clamp(parseInt(evt.target.value, 10), 0, 100);
try {
const cHex = colorFn.hsl_to_hex(h, divide(value, 100), divide(l, 100));
setS(value);
setHex(cHex);
updateRGB(cHex);
onChange(cHex);
} catch (e) {
console.error('[Update Saturation]', e, value);
}
};
const updateL = (evt: ChangeEvent<HTMLInputElement>) => {
const value = clamp(parseInt(evt.target.value, 10), 0, 100);
try {
const cHex = colorFn.hsl_to_hex(h, divide(s, 100), divide(value, 100));
setL(value);
setHex(cHex);
updateRGB(cHex);
onChange(cHex);
} catch (e) {
console.error('[Update Lightness]', e, value);
}
};
useEffect(() => {
try {
const [r, g, b] = colorFn?.represent_rgb(color) ?? [0, 0, 0];
setR(r);
setG(g);
setB(b);
const [h, s, l] = colorFn?.represent_hsl(color) ?? [0, 0, 0];
setH(floor(h, 1));
setS(floor(times(s, 100), 2));
setL(floor(times(l, 100), 2));
} catch (e) {
console.error('[Convert RGB]', e);
}
}, []);
return (
<div className={styles.rgb_input}>
<div className={cx('input_wrapper')}>
<Icon icon="tabler:hash" />
<input type="text" value={hex} onChange={updateHex} className={styles.rgb_input} />
</div>
<div className={styles.component_row}>
<div className={cx('input_wrapper', styles.component_input)}>
<Icon icon="tabler:letter-r" />
<input
type="number"
min={0}
max={255}
value={r}
onChange={updateR}
className={styles.rgb_input}
/>
</div>
<div className={cx('input_wrapper', styles.component_input)}>
<Icon icon="tabler:letter-g" />
<input
type="number"
min={0}
max={255}
value={g}
onChange={updateG}
className={styles.rgb_input}
/>
</div>
<div className={cx('input_wrapper', styles.component_input)}>
<Icon icon="tabler:letter-b" />
<input
type="number"
min={0}
max={255}
value={b}
onChange={updateB}
className={styles.rgb_input}
/>
</div>
</div>
<div className={styles.component_row}>
<div className={cx('input_wrapper', styles.component_input)}>
<Icon icon="tabler:letter-h" />
<input
type="number"
min={0}
max={360}
value={h}
onChange={updateH}
className={styles.rgb_input}
/>
<span>&deg;</span>
</div>
<div className={cx('input_wrapper', styles.component_input)}>
<Icon icon="tabler:letter-s" />
<input
type="number"
min={0}
max={100}
value={s}
onChange={updateS}
className={styles.rgb_input}
/>
<Icon icon="tabler:percentage" />
</div>
<div className={cx('input_wrapper', styles.component_input)}>
<Icon icon="tabler:letter-l" />
<input
type="number"
min={0}
max={100}
value={l}
onChange={updateL}
className={styles.rgb_input}
/>
<Icon icon="tabler:percentage" />
</div>
</div>
</div>
);
}