import React, { useEffect } from 'react';
import { getCircle } from '../../lib/paint';
import { ShadowDomContext } from '../../shadow-dom';
import { Input } from '../ui/input';
import { Button } from '../ui/button';
import { StyledPicker } from '../color-picker';
import { ErrorBoundary } from 'react-error-boundary';
export const PixelCursor = (props: { color: string; dia: number; factor: number; style?: React.CSSProperties; className?: string }) => {
const { className, color, dia, factor, style } = props;
const pixels = React.useMemo(() => {
const result: [number, number, number][] = new Array(dia).fill(0).map((_, i) => [i, Infinity, -1]);
getCircle(dia).forEach(([x, y]) => {
result[y][1] = Math.min(x, result[y][1]);
result[y][2] = Math.max(x, result[y][1]);
});
return result.filter((x) => x[1] !== Infinity);
}, [dia]);
return (
{pixels.map(([y, from, to]) => (
))}
);
};
export type CursorDef =
| {
type: 'circle';
dia: number;
factor: number;
}
| {
type: 'rect';
x: number;
xFactor: number;
y: number;
yFactor: number;
};
export const PixelContainer = (props: {
color: string;
cursor: CursorDef;
offsetX: number;
offsetY: number;
children?: React.ReactNode | Iterable;
showPreview?: boolean;
}) => {
const { color, cursor, offsetX, offsetY, children, showPreview } = props;
const [cursorPos, setCursorPos] = React.useState<[number, number] | null>(null);
return (
setCursorPos(null)}
onMouseMoveCapture={(e) => setCursorPos([e.nativeEvent.offsetX, e.nativeEvent.offsetY])}
onTouchMoveCapture={(e) => {
const rect = e.currentTarget.getBoundingClientRect();
setCursorPos([e.changedTouches[0].pageX - rect.left, e.changedTouches[0].pageY - rect.top]);
}}
onTouchEnd={() => setCursorPos(null)}
>
{children}
{cursorPos !== null && cursor.type === 'rect' && (
<>
>
)}
{showPreview && !cursorPos && cursor.type === 'rect' && (
)}
{cursorPos !== null && cursor.type === 'circle' && (
)}
{showPreview && !cursorPos && cursor.type === 'circle' && (
)}
);
};
export const ClickInput = (props: { value: string; onChange: (v: string) => void }) => {
const [edit, setEdit] = React.useState(false);
const doChange = (e: { target: HTMLInputElement }) => {
if (e.target.value) props.onChange(e.target.value);
setEdit(false);
};
return edit ? (
{
if (e.key === 'Enter' && !e.nativeEvent.isComposing) {
doChange(e as unknown as { target: HTMLInputElement });
return false;
} else {
return true;
}
}}
/>
) : (
);
};
export const ColorEditor = (props: { color: string; onChangeColor: (color: string) => void; colors: string[] }) => {
const [showColorEdit, setShowColorEdit] = React.useState(false);
const [colorEdited, setColorEdited] = React.useState(false);
const ref = React.useRef(null);
const shadowDomMeta = React.useContext(ShadowDomContext);
useEffect(() => {
if (showColorEdit) {
const listener = (e: Event) => {
if (e.target && !ref.current?.contains(e.target as Node)) {
setShowColorEdit(false);
}
};
const dom = shadowDomMeta.root;
dom?.addEventListener('click', listener);
return () => dom?.removeEventListener('click', listener);
}
}, [showColorEdit]);
const [color, setColor] = React.useState(props.color);
return (
{
props.onChangeColor(e.target.value);
}}
/>
}
>
{
e.stopPropagation();
e.preventDefault();
setShowColorEdit(true);
setColorEdited(false);
setColor(props.color);
}}
>
{showColorEdit && (
{
setColorEdited(true);
setColor(color.hex);
}}
noShadow
noBorder
/>
)}
);
};