/**
* OutputEditor — the per-output menu (state · min · max · static value · curve).
* Opens on hover over an output column/cell. Ported from `OutputEditor.jsx`.
*/
import { useLayoutEffect, useRef, useState } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import { CurvePad } from './CurvePad';
import type { MFParam, ParamStatus } from './model';
const OE_STATUS: { v: ParamStatus; label: string; color: string }[] = [
{ v: 'off', label: 'Off', color: 'var(--fg-dim)' },
{ v: 'fixed', label: 'Fixed', color: 'var(--accent-2)' },
{ v: 'live', label: 'Live', color: 'var(--accent)' },
];
function MiniSlider({
label,
value,
onChange,
disabled,
}: {
label: ReactNode;
value: number;
onChange: (v: number) => void;
disabled?: boolean;
}) {
return (
);
}
export interface OutputEditorProps {
param: MFParam;
onChange: (patch: Partial) => void;
onHold: () => void;
onLeave: () => void;
place: CSSProperties;
}
export function OutputEditor({ param, onChange, onHold, onLeave, place }: OutputEditorProps) {
const isLive = param.status === 'live';
const ref = useRef(null);
// After the popover lands at its anchored spot, nudge it back on-screen so it
// never clips the viewport edges (matters when the window isn't full-screen).
const [shift, setShift] = useState<{ dx: number; dy: number }>({ dx: 0, dy: 0 });
useLayoutEffect(() => {
const el = ref.current;
if (!el) return;
const fit = () => {
const r = el.getBoundingClientRect();
// r already includes the current shift — back it out to get the natural
// anchored box, then derive the shift that pulls it inside the viewport.
const left = r.left - shift.dx;
const top = r.top - shift.dy;
const M = 8; // keep an 8px gutter from each edge
let dx = 0;
let dy = 0;
if (left + r.width > window.innerWidth - M) dx = window.innerWidth - M - (left + r.width);
if (left + dx < M) dx = M - left;
if (top + r.height > window.innerHeight - M) dy = window.innerHeight - M - (top + r.height);
if (top + dy < M) dy = M - top; // if too tall, prefer pinning the top
if (dx !== shift.dx || dy !== shift.dy) setShift({ dx, dy });
};
fit();
window.addEventListener('resize', fit);
return () => window.removeEventListener('resize', fit);
});
return (
onChange({ min: v })} />
onChange({ max: v })} />
onChange({ val: v })}
disabled={isLive}
/>
onChange({ curve: c })} size={170} />
drag a bar to set value · ⌥/alt-click cycles state