Add widget-control logic action and multi-column CSV / multi-field dialogs
Extend the panel logic editor with two capabilities: - action.widget: drive a panel widget by id from a flow — enable/disable, show/hide, and (for plot widgets) clear, pause, or resume the live plot. Wired via a per-widget command store consumed by a Canvas WidgetView wrapper (disable overlay / hide) and PlotWidget (pause/clear), reset on panel unmount. - action.export now emits multiple named arrays as CSV columns with per-column labels and an alignment mode (common/any/interpolate); dialogs support asking for and displaying multiple values per dialog. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+38
-4
@@ -1,7 +1,8 @@
|
||||
import { h } from 'preact';
|
||||
import { h, Fragment } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
import { initLocalState } from './lib/localstate';
|
||||
import { logicEngine } from './lib/logic';
|
||||
import { getWidgetCmdStore, type WidgetCmd } from './lib/widgetCommands';
|
||||
import type { Interface, Widget, SignalRef } from './lib/types';
|
||||
import TextView from './widgets/TextView';
|
||||
import TextLabel from './widgets/TextLabel';
|
||||
@@ -42,6 +43,32 @@ interface CtxState {
|
||||
signal: SignalRef | null;
|
||||
}
|
||||
|
||||
// Wraps a single view-mode widget so panel logic (action.widget) can drive it:
|
||||
// `hidden` removes it from the view, `disabled` dims it and blocks interaction
|
||||
// via a transparent overlay (which still surfaces the right-click menu). Plot
|
||||
// pause/clear are handled inside PlotWidget itself.
|
||||
function WidgetView({ widget, children, onContextMenu }: {
|
||||
widget: Widget;
|
||||
children: any;
|
||||
onContextMenu: (e: MouseEvent) => void;
|
||||
key?: string;
|
||||
}) {
|
||||
const [cmd, setCmd] = useState<WidgetCmd>(() => getWidgetCmdStore(widget.id).get());
|
||||
useEffect(() => getWidgetCmdStore(widget.id).subscribe(setCmd), [widget.id]);
|
||||
if (cmd.hidden) return null;
|
||||
if (!cmd.disabled) return children;
|
||||
return (
|
||||
<Fragment>
|
||||
{children}
|
||||
<div
|
||||
class="widget-disable-overlay"
|
||||
style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
|
||||
onContextMenu={onContextMenu}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
iface: Interface | null;
|
||||
onNavigate?: (interfaceId: string) => void;
|
||||
@@ -131,9 +158,8 @@ export default function Canvas({ iface, onNavigate, timeRange }: Props) {
|
||||
<div class="canvas-area" style={`width:${iface.w}px;height:${iface.h}px;`}>
|
||||
{iface.widgets.map(widget => {
|
||||
const Comp = COMPONENTS[widget.type];
|
||||
return Comp
|
||||
const inner = Comp
|
||||
? <Comp
|
||||
key={widget.id}
|
||||
widget={widget}
|
||||
onContextMenu={(e: MouseEvent) => onCtxMenu(e, widget)}
|
||||
onNavigate={onNavigate}
|
||||
@@ -141,7 +167,6 @@ export default function Canvas({ iface, onNavigate, timeRange }: Props) {
|
||||
/>
|
||||
: (
|
||||
<div
|
||||
key={widget.id}
|
||||
class="unknown-widget"
|
||||
style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
|
||||
title={`Unknown widget type: ${widget.type}`}
|
||||
@@ -150,6 +175,15 @@ export default function Canvas({ iface, onNavigate, timeRange }: Props) {
|
||||
<span class="unknown-label">{widget.type}</span>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<WidgetView
|
||||
key={widget.id}
|
||||
widget={widget}
|
||||
onContextMenu={(e: MouseEvent) => onCtxMenu(e, widget)}
|
||||
>
|
||||
{inner}
|
||||
</WidgetView>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user