fix(editor): array local-variable support in signal-tree pane

The signal-tree pane (edit mode) has its own inline "add local variable"
form, separate from the Logic-tab LocalVars form that gained array support
in the array-locals feature. This second entry point was missed, so the
type dropdown there only offered number/bool/string. Mirror the LogicEditor
form: add the array type option plus elem/sizing/capacity selects and a
JSON-validated initial value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino Ferrari
2026-06-24 13:59:18 +02:00
parent 9d9e538a90
commit cb4e81beb2
+72 -5
View File
@@ -117,17 +117,48 @@ export default function SignalTree({ onDragStart, width, panelId, statevars, onS
const [showAddLocal, setShowAddLocal] = useState(false); const [showAddLocal, setShowAddLocal] = useState(false);
const [localOpen, setLocalOpen] = useState(true); const [localOpen, setLocalOpen] = useState(true);
const [lvName, setLvName] = useState(''); const [lvName, setLvName] = useState('');
const [lvType, setLvType] = useState<'number' | 'bool' | 'string'>('number'); const [lvType, setLvType] = useState<'number' | 'bool' | 'string' | 'array'>('number');
const [lvInitial, setLvInitial] = useState('0'); const [lvInitial, setLvInitial] = useState('0');
// array-specific fields (mirror LogicEditor LocalVars)
const [lvElem, setLvElem] = useState<'number' | 'bool' | 'array'>('number');
const [lvSizing, setLvSizing] = useState<'dynamic' | 'capped' | 'fixed'>('dynamic');
const [lvCapacity, setLvCapacity] = useState('100');
const [lvJsonErr, setLvJsonErr] = useState('');
function handleLvTypeChange(t: 'number' | 'bool' | 'string' | 'array') {
setLvType(t);
setLvInitial(t === 'array' ? '[]' : t === 'bool' ? 'false' : t === 'string' ? '' : '0');
setLvJsonErr('');
}
function handleLvInitialChange(v: string) {
setLvInitial(v);
if (lvType === 'array') {
try { JSON.parse(v); setLvJsonErr(''); }
catch (e: any) { setLvJsonErr(e.message); }
}
}
function addLocal() { function addLocal() {
const name = lvName.trim(); const name = lvName.trim();
if (!name || !onStateVarsChange) return; if (!name || !onStateVarsChange) return;
if (lvType === 'array' && lvJsonErr) return;
const base: StateVar = { name, type: lvType, initial: lvInitial };
if (lvType === 'array') {
base.elem = lvElem;
base.sizing = lvSizing;
if (lvSizing !== 'dynamic') base.capacity = parseInt(lvCapacity, 10) || 100;
}
const next = (statevars ?? []).filter(v => v.name !== name); const next = (statevars ?? []).filter(v => v.name !== name);
next.push({ name, type: lvType, initial: lvInitial }); next.push(base);
onStateVarsChange(next); onStateVarsChange(next);
setLvName(''); setLvName('');
setLvInitial('0'); setLvInitial('0');
setLvType('number');
setLvElem('number');
setLvSizing('dynamic');
setLvCapacity('100');
setLvJsonErr('');
setShowAddLocal(false); setShowAddLocal(false);
} }
@@ -324,20 +355,56 @@ export default function SignalTree({ onDragStart, width, panelId, statevars, onS
class="prop-select" class="prop-select"
style="font-size: 0.7rem; height: 1.6rem; padding: 0 4px;" style="font-size: 0.7rem; height: 1.6rem; padding: 0 4px;"
value={lvType} value={lvType}
onChange={(e) => setLvType((e.target as HTMLSelectElement).value as any)} onChange={(e) => handleLvTypeChange((e.target as HTMLSelectElement).value as any)}
> >
<option value="number">number</option> <option value="number">number</option>
<option value="bool">bool</option> <option value="bool">bool</option>
<option value="string">string</option> <option value="string">string</option>
<option value="array">array</option>
</select> </select>
{lvType === 'array' && (
<Fragment>
<select
class="prop-select"
style="font-size: 0.7rem; height: 1.6rem; padding: 0 4px;"
value={lvElem}
onChange={(e) => setLvElem((e.target as HTMLSelectElement).value as any)}
>
<option value="number">elem: number</option>
<option value="bool">elem: bool</option>
<option value="array">elem: array</option>
</select>
<select
class="prop-select"
style="font-size: 0.7rem; height: 1.6rem; padding: 0 4px;"
value={lvSizing}
onChange={(e) => setLvSizing((e.target as HTMLSelectElement).value as any)}
>
<option value="dynamic">sizing: dynamic</option>
<option value="capped">sizing: capped</option>
<option value="fixed">sizing: fixed</option>
</select>
{lvSizing !== 'dynamic' && (
<input <input
class="signal-add-input" class="signal-add-input"
type="number"
style="width: 5rem;"
placeholder="capacity"
value={lvCapacity}
onInput={(e) => setLvCapacity((e.target as HTMLInputElement).value)}
/>
)}
</Fragment>
)}
<input
class={`signal-add-input${lvJsonErr ? ' prop-input-error' : ''}`}
style="flex: 1;" style="flex: 1;"
placeholder="initial" placeholder={lvType === 'array' ? 'initial (JSON)' : 'initial'}
value={lvInitial} value={lvInitial}
onInput={(e) => setLvInitial((e.target as HTMLInputElement).value)} onInput={(e) => handleLvInitialChange((e.target as HTMLInputElement).value)}
onKeyDown={(e: KeyboardEvent) => { if (e.key === 'Enter') addLocal(); }} onKeyDown={(e: KeyboardEvent) => { if (e.key === 'Enter') addLocal(); }}
/> />
{lvJsonErr && <p class="wizard-error" style="flex-basis: 100%; margin: 0;">{lvJsonErr}</p>}
<button class="icon-btn" title="Add" onClick={addLocal}></button> <button class="icon-btn" title="Add" onClick={addLocal}></button>
<button class="icon-btn" title="Cancel" onClick={() => setShowAddLocal(false)}></button> <button class="icon-btn" title="Cancel" onClick={() => setShowAddLocal(false)}></button>
</div> </div>