color-q/src/components/ColorStand.tsx

116 lines
3.4 KiB
TypeScript

import { Icon } from '@iconify/react/dist/iconify.js';
import { isNil } from 'lodash-es';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import NoColor from '../assets/NoColor.svg';
import { useColorFunction } from '../ColorFunctionContext';
import styles from './ColorStand.module.css';
import { NotificationType, useNotification } from './Notifications';
import { SegmentedControl } from './SegmentedControl';
type ColorValueProps = {
value: string | null;
};
function ColorValue({ value }: ColorValueProps) {
const [cpState, copyToClipboard] = useCopyToClipboard();
const { showToast } = useNotification();
const handleCopy = useCallback(() => {
if (!isNil(value)) {
copyToClipboard(value);
}
}, [value]);
useEffect(() => {
if (!isNil(cpState.error)) {
showToast(NotificationType.ERROR, 'Failed to copy to clipboard', 'tabler:alert-circle', 3000);
} else if (!isNil(cpState.value)) {
showToast(
NotificationType.SUCCESS,
`${cpState.value} has been copied to clipboard.`,
'tabler:circle-check',
3000,
);
}
}, [cpState]);
return (
<div className={styles.color_value}>
{isNil(value) ? (
<div className={styles.na_value}>
<Icon icon="tabler:alert-hexagon" />
<span>Not Available</span>
</div>
) : (
<div className={styles.value} onClick={handleCopy}>
{value}
</div>
)}
</div>
);
}
type ColorStandProps = {
title?: string;
color?: string;
};
export function ColorStand({ title, color }: ColorStandProps) {
const { colorFn } = useColorFunction();
const [viewColor, setViewColor] = useState<'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch'>('hex');
const displayColorValue = useMemo(() => {
if (isNil(color)) {
return null;
}
switch (viewColor) {
case 'hex':
return color;
case 'rgb': {
const rgb = colorFn?.represent_rgb(color);
return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
}
case 'hsl': {
const hsl = colorFn?.represent_hsl(color);
return `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`;
}
case 'lab': {
const lab = colorFn?.represent_lab(color);
return `lab(${lab[0]}, ${lab[1]}, ${lab[2]})`;
}
case 'oklch': {
const oklch = colorFn?.represent_oklch(color);
return `oklch(${oklch[0]}%, ${oklch[1]}, ${oklch[2]})`;
}
default:
return null;
}
}, [viewColor, color]);
return (
<div className={styles.color_stand}>
<div className={styles.preview_box}>
<div className={styles.head_line}>
<h5>{title}</h5>
</div>
<div className={styles.color_box} style={{ backgroundColor: color }}>
<img src={NoColor} alt="No Color" />
</div>
</div>
<div className={styles.color_describe}>
<SegmentedControl
options={[
{ label: 'HEX', value: 'hex' },
{ label: 'RGB', value: 'rgb' },
{ label: 'HSL', value: 'hsl' },
{ label: 'LAB', value: 'lab' },
{ label: 'OKLCH', value: 'oklch' },
]}
value={viewColor}
onChange={(value) => setViewColor(value as typeof viewColor)}
/>
<ColorValue value={displayColorValue} />
</div>
</div>
);
}