import { type Component, createSignal } from 'solid-js';
interface Props {
value: number;
tint: string; // CSS color used to tint a positive (attraction) cell
onChange: (value: number) => void;
}
const MIN = -2;
const MAX = 2;
const RANGE = MAX - MIN;
function clamp(v: number): number {
return Math.max(MIN, Math.min(MAX, Math.round(v / 0.05) * 0.05));
}
export const MatrixCell: Component<Props> = (props) => {
const [dragging, setDragging] = createSignal(false);
const onPointerDown = (e: PointerEvent) => {
e.preventDefault();
const el = e.currentTarget as HTMLElement;
el.setPointerCapture(e.pointerId);
setDragging(true);
const startY = e.clientY;
const startVal = props.value;
const onMove = (ev: PointerEvent) => {
const dy = startY - ev.clientY;
props.onChange(clamp(startVal + (dy / 200) * RANGE));
};
const onUp = (ev: PointerEvent) => {
el.releasePointerCapture(ev.pointerId);
setDragging(false);
el.removeEventListener('pointermove', onMove);
el.removeEventListener('pointerup', onUp);
};
el.addEventListener('pointermove', onMove);
el.addEventListener('pointerup', onUp);
};
const onKeyDown = (e: KeyboardEvent) => {
if (e.key === 'ArrowUp') {
props.onChange(clamp(props.value + 0.1));
e.preventDefault();
} else if (e.key === 'ArrowDown') {
props.onChange(clamp(props.value - 0.1));
e.preventDefault();
}
};
const intensity = () => Math.min(1, Math.abs(props.value) / 2);
const bg = () =>
props.value >= 0
? `color-mix(in oklab, ${props.tint} ${intensity() * 30}%, var(--surface-inset))`
: `color-mix(in oklab, var(--signal-crimson) ${intensity() * 30}%, var(--surface-inset))`;
return (
<div
role="slider"
tabIndex={0}
aria-valuenow={props.value}
aria-valuemin={MIN}
aria-valuemax={MAX}
class="phys-num"
onPointerDown={onPointerDown}
onKeyDown={onKeyDown}
style={{
height: '34px',
display: 'flex',
'align-items': 'center',
'justify-content': 'center',
background: bg(),
border: `1px solid ${dragging() ? 'var(--accent)' : 'var(--border-subtle)'}`,
'border-radius': 'var(--radius-md)',
'font-size': '11px',
color: 'var(--fg-primary)',
cursor: 'ns-resize',
'user-select': 'none',
'touch-action': 'none',
}}
>
{props.value >= 0 ? '+' : ''}
{props.value.toFixed(2)}
</div>
);
};