Implemented confi snapshots
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import { h } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
import { wsClient } from '../lib/ws';
|
||||
import { getSignalStore } from '../lib/stores';
|
||||
import type { Widget, SignalValue } from '../lib/types';
|
||||
|
||||
interface InstanceMeta { id: string; name: string; setId?: string; }
|
||||
|
||||
interface Props { widget: Widget; onContextMenu?: (e: MouseEvent) => void; }
|
||||
|
||||
// A combo that lists the config instances of a chosen set and writes the
|
||||
// selected instance id (a string) to a panel-local variable. The output var can
|
||||
// then drive the apply/read/write config logic nodes (set to "From variable"),
|
||||
// letting an operator pick which configuration the flow operates on at run time.
|
||||
export default function ConfigSelect({ widget, onContextMenu }: Props) {
|
||||
const setId = widget.options['set'] || '';
|
||||
const output = widget.options['output'] || '';
|
||||
const label = widget.options['label'] || 'Config';
|
||||
// Optional comma-separated allow-list of instance ids; empty shows them all.
|
||||
const subset = (widget.options['instances'] || '')
|
||||
.split(',').map(s => s.trim()).filter(Boolean);
|
||||
|
||||
const [instances, setInstances] = useState<InstanceMeta[]>([]);
|
||||
const [selected, setSelected] = useState('');
|
||||
|
||||
// Mirror the output var so external writers (e.g. a create-config node) keep
|
||||
// the combo in sync.
|
||||
useEffect(() => {
|
||||
if (!output) return;
|
||||
const unsub = getSignalStore({ ds: 'local', name: output }).subscribe((v: SignalValue) => {
|
||||
const id = v.value == null ? '' : String(v.value);
|
||||
setSelected(prev => (id && id !== prev ? id : prev));
|
||||
});
|
||||
return unsub;
|
||||
}, [output]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
fetch('/api/v1/config/instances')
|
||||
.then(r => r.ok ? r.json() : [])
|
||||
.then((list: InstanceMeta[]) => {
|
||||
if (cancelled) return;
|
||||
let filtered = (list ?? []).filter(ci => !setId || ci.setId === setId);
|
||||
if (subset.length > 0) filtered = filtered.filter(ci => subset.includes(ci.id));
|
||||
setInstances(filtered);
|
||||
// Default the output to the first instance when nothing is chosen yet.
|
||||
setSelected(prev => {
|
||||
if (prev && filtered.some(ci => ci.id === prev)) return prev;
|
||||
const first = filtered[0]?.id ?? '';
|
||||
if (first && output) wsClient.write({ ds: 'local', name: output }, first);
|
||||
return first;
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
return () => { cancelled = true; };
|
||||
}, [setId, widget.options['instances']]);
|
||||
|
||||
function onChange(id: string) {
|
||||
setSelected(id);
|
||||
if (output) wsClient.write({ ds: 'local', name: output }, id);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class="configselect"
|
||||
style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
<span class="cs-label">{label}:</span>
|
||||
<select
|
||||
class="cs-select"
|
||||
value={selected}
|
||||
onChange={(e: Event) => onChange((e.target as HTMLSelectElement).value)}
|
||||
>
|
||||
{instances.length === 0 && <option value="">no instances</option>}
|
||||
{instances.map(ci => <option key={ci.id} value={ci.id}>{ci.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user